2018-06-24 15:54:33 +02:00
/*
* MFRC522 . cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS I2C BY AROZCAN
* MFRC522 . cpp - Based on ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI Library BY COOQROBOT .
* NOTE : Please also check the comments in MFRC522 . h - they provide useful hints and background information .
* Released into the public domain .
2018-06-24 16:00:14 +02:00
* Author : arozcan @ https : //github.com/arozcan/MFRC522-I2C-Library
2018-06-24 15:54:33 +02:00
*/
# include <Arduino.h>
# include <MFRC522_I2C.h>
# include <Wire.h>
/////////////////////////////////////////////////////////////////////////////////////
// Functions for setting up the Arduino
/////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor .
* Prepares the output pins .
*/
MFRC522 : : MFRC522 ( byte chipAddress ,
byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low)
) {
_chipAddress = chipAddress ;
_resetPowerDownPin = resetPowerDownPin ;
} // End constructor
/////////////////////////////////////////////////////////////////////////////////////
// Basic interface functions for communicating with the MFRC522
/////////////////////////////////////////////////////////////////////////////////////
/**
* Writes a byte to the specified register in the MFRC522 chip .
* The interface is described in the datasheet section 8.1 .2 .
*/
void MFRC522 : : PCD_WriteRegister ( byte reg , ///< The register to write to. One of the PCD_Register enums.
byte value ///< The value to write.
) {
Wire . beginTransmission ( _chipAddress ) ;
Wire . write ( reg ) ;
Wire . write ( value ) ;
Wire . endTransmission ( ) ;
} // End PCD_WriteRegister()
/**
* Writes a number of bytes to the specified register in the MFRC522 chip .
* The interface is described in the datasheet section 8.1 .2 .
*/
void MFRC522 : : PCD_WriteRegister ( byte reg , ///< The register to write to. One of the PCD_Register enums.
byte count , ///< The number of bytes to write to the register
byte * values ///< The values to write. Byte array.
) {
Wire . beginTransmission ( _chipAddress ) ;
Wire . write ( reg ) ;
for ( byte index = 0 ; index < count ; index + + ) {
Wire . write ( values [ index ] ) ;
}
Wire . endTransmission ( ) ;
} // End PCD_WriteRegister()
/**
* Reads a byte from the specified register in the MFRC522 chip .
* The interface is described in the datasheet section 8.1 .2 .
*/
byte MFRC522 : : PCD_ReadRegister ( byte reg ///< The register to read from. One of the PCD_Register enums.
) {
byte value ;
//digitalWrite(_chipSelectPin, LOW); // Select slave
Wire . beginTransmission ( _chipAddress ) ;
Wire . write ( reg ) ;
Wire . endTransmission ( ) ;
Wire . requestFrom ( _chipAddress , 1 ) ;
value = Wire . read ( ) ;
return value ;
} // End PCD_ReadRegister()
/**
* Reads a number of bytes from the specified register in the MFRC522 chip .
* The interface is described in the datasheet section 8.1 .2 .
*/
void MFRC522 : : PCD_ReadRegister ( byte reg , ///< The register to read from. One of the PCD_Register enums.
byte count , ///< The number of bytes to read
byte * values , ///< Byte array to store the values in.
byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated.
) {
if ( count = = 0 ) {
return ;
}
byte address = reg ;
byte index = 0 ; // Index in values array.
Wire . beginTransmission ( _chipAddress ) ;
Wire . write ( address ) ;
Wire . endTransmission ( ) ;
Wire . requestFrom ( _chipAddress , count ) ;
while ( Wire . available ( ) ) {
if ( index = = 0 & & rxAlign ) { // Only update bit positions rxAlign..7 in values[0]
// Create bit mask for bit positions rxAlign..7
byte mask = 0 ;
for ( byte i = rxAlign ; i < = 7 ; i + + ) {
mask | = ( 1 < < i ) ;
}
// Read value and tell that we want to read the same address again.
byte value = Wire . read ( ) ;
// Apply mask to both current value of values[0] and the new data in value.
values [ 0 ] = ( values [ index ] & ~ mask ) | ( value & mask ) ;
}
else { // Normal case
values [ index ] = Wire . read ( ) ;
}
index + + ;
}
} // End PCD_ReadRegister()
/**
* Sets the bits given in mask in register reg .
*/
void MFRC522 : : PCD_SetRegisterBitMask ( byte reg , ///< The register to update. One of the PCD_Register enums.
byte mask ///< The bits to set.
) {
byte tmp ;
tmp = PCD_ReadRegister ( reg ) ;
PCD_WriteRegister ( reg , tmp | mask ) ; // set bit mask
} // End PCD_SetRegisterBitMask()
/**
* Clears the bits given in mask from register reg .
*/
void MFRC522 : : PCD_ClearRegisterBitMask ( byte reg , ///< The register to update. One of the PCD_Register enums.
byte mask ///< The bits to clear.
) {
byte tmp ;
tmp = PCD_ReadRegister ( reg ) ;
PCD_WriteRegister ( reg , tmp & ( ~ mask ) ) ; // clear bit mask
} // End PCD_ClearRegisterBitMask()
/**
* Use the CRC coprocessor in the MFRC522 to calculate a CRC_A .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PCD_CalculateCRC ( byte * data , ///< In: Pointer to the data to transfer to the FIFO for CRC calculation.
byte length , ///< In: The number of bytes to transfer.
byte * result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first.
) {
PCD_WriteRegister ( CommandReg , PCD_Idle ) ; // Stop any active command.
PCD_WriteRegister ( DivIrqReg , 0x04 ) ; // Clear the CRCIRq interrupt request bit
PCD_SetRegisterBitMask ( FIFOLevelReg , 0x80 ) ; // FlushBuffer = 1, FIFO initialization
PCD_WriteRegister ( FIFODataReg , length , data ) ; // Write data to the FIFO
PCD_WriteRegister ( CommandReg , PCD_CalcCRC ) ; // Start the calculation
// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73<EFBFBD> s.
word i = 5000 ;
byte n ;
while ( 1 ) {
n = PCD_ReadRegister ( DivIrqReg ) ; // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
if ( n & 0x04 ) { // CRCIRq bit set - calculation done
break ;
}
if ( - - i = = 0 ) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down.
return STATUS_TIMEOUT ;
}
}
PCD_WriteRegister ( CommandReg , PCD_Idle ) ; // Stop calculating CRC for new content in the FIFO.
// Transfer the result from the registers to the result buffer
result [ 0 ] = PCD_ReadRegister ( CRCResultRegL ) ;
result [ 1 ] = PCD_ReadRegister ( CRCResultRegH ) ;
return STATUS_OK ;
} // End PCD_CalculateCRC()
/////////////////////////////////////////////////////////////////////////////////////
// Functions for manipulating the MFRC522
/////////////////////////////////////////////////////////////////////////////////////
/**
* Initializes the MFRC522 chip .
*/
void MFRC522 : : PCD_Init ( ) {
// Set the chipSelectPin as digital output, do not select the slave yet
// Set the resetPowerDownPin as digital output, do not reset or power down.
pinMode ( _resetPowerDownPin , OUTPUT ) ;
if ( digitalRead ( _resetPowerDownPin ) = = LOW ) { //The MFRC522 chip is in power down mode.
digitalWrite ( _resetPowerDownPin , HIGH ) ; // Exit power down mode. This triggers a hard reset.
// Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74<37> s. Let us be generous: 50ms.
delay ( 50 ) ;
}
else { // Perform a soft reset
PCD_Reset ( ) ;
}
// When communicating with a PICC we need a timeout if something goes wrong.
// f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo].
// TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg.
PCD_WriteRegister ( TModeReg , 0x80 ) ; // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
PCD_WriteRegister ( TPrescalerReg , 0xA9 ) ; // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25<32> s.
PCD_WriteRegister ( TReloadRegH , 0x03 ) ; // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.
PCD_WriteRegister ( TReloadRegL , 0xE8 ) ;
PCD_WriteRegister ( TxASKReg , 0x40 ) ; // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
PCD_WriteRegister ( ModeReg , 0x3D ) ; // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
PCD_AntennaOn ( ) ; // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
} // End PCD_Init()
/**
* Performs a soft reset on the MFRC522 chip and waits for it to be ready again .
*/
void MFRC522 : : PCD_Reset ( ) {
PCD_WriteRegister ( CommandReg , PCD_SoftReset ) ; // Issue the SoftReset command.
// The datasheet does not mention how long the SoftRest command takes to complete.
// But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg)
// Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74<37> s. Let us be generous: 50ms.
delay ( 50 ) ;
// Wait for the PowerDown bit in CommandReg to be cleared
while ( PCD_ReadRegister ( CommandReg ) & ( 1 < < 4 ) ) {
// PCD still restarting - unlikely after waiting 50ms, but better safe than sorry.
}
} // End PCD_Reset()
/**
* Turns the antenna on by enabling pins TX1 and TX2 .
* After a reset these pins are disabled .
*/
void MFRC522 : : PCD_AntennaOn ( ) {
byte value = PCD_ReadRegister ( TxControlReg ) ;
if ( ( value & 0x03 ) ! = 0x03 ) {
PCD_WriteRegister ( TxControlReg , value | 0x03 ) ;
}
} // End PCD_AntennaOn()
/**
* Turns the antenna off by disabling pins TX1 and TX2 .
*/
void MFRC522 : : PCD_AntennaOff ( ) {
PCD_ClearRegisterBitMask ( TxControlReg , 0x03 ) ;
} // End PCD_AntennaOff()
/**
* Get the current MFRC522 Receiver Gain ( RxGain [ 2 : 0 ] ) value .
* See 9.3 .3 .6 / table 98 in http : //www.nxp.com/documents/data_sheet/MFRC522.pdf
* NOTE : Return value scrubbed with ( 0x07 < < 4 ) = 01110000 b as RCFfgReg may use reserved bits .
*
* @ return Value of the RxGain , scrubbed to the 3 bits used .
*/
byte MFRC522 : : PCD_GetAntennaGain ( ) {
return PCD_ReadRegister ( RFCfgReg ) & ( 0x07 < < 4 ) ;
} // End PCD_GetAntennaGain()
/**
* Set the MFRC522 Receiver Gain ( RxGain ) to value specified by given mask .
* See 9.3 .3 .6 / table 98 in http : //www.nxp.com/documents/data_sheet/MFRC522.pdf
* NOTE : Given mask is scrubbed with ( 0x07 < < 4 ) = 01110000 b as RCFfgReg may use reserved bits .
*/
void MFRC522 : : PCD_SetAntennaGain ( byte mask ) {
if ( PCD_GetAntennaGain ( ) ! = mask ) { // only bother if there is a change
PCD_ClearRegisterBitMask ( RFCfgReg , ( 0x07 < < 4 ) ) ; // clear needed to allow 000 pattern
PCD_SetRegisterBitMask ( RFCfgReg , mask & ( 0x07 < < 4 ) ) ; // only set RxGain[2:0] bits
}
} // End PCD_SetAntennaGain()
/**
* Performs a self - test of the MFRC522
* See 16.1 .1 in http : //www.nxp.com/documents/data_sheet/MFRC522.pdf
*
* @ return Whether or not the test passed .
*/
bool MFRC522 : : PCD_PerformSelfTest ( ) {
// This follows directly the steps outlined in 16.1.1
// 1. Perform a soft reset.
PCD_Reset ( ) ;
// 2. Clear the internal buffer by writing 25 bytes of 00h
byte ZEROES [ 25 ] = { 0x00 } ;
PCD_SetRegisterBitMask ( FIFOLevelReg , 0x80 ) ; // flush the FIFO buffer
PCD_WriteRegister ( FIFODataReg , 25 , ZEROES ) ; // write 25 bytes of 00h to FIFO
PCD_WriteRegister ( CommandReg , PCD_Mem ) ; // transfer to internal buffer
// 3. Enable self-test
PCD_WriteRegister ( AutoTestReg , 0x09 ) ;
// 4. Write 00h to FIFO buffer
PCD_WriteRegister ( FIFODataReg , 0x00 ) ;
// 5. Start self-test by issuing the CalcCRC command
PCD_WriteRegister ( CommandReg , PCD_CalcCRC ) ;
// 6. Wait for self-test to complete
word i ;
byte n ;
for ( i = 0 ; i < 0xFF ; i + + ) {
n = PCD_ReadRegister ( DivIrqReg ) ; // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
if ( n & 0x04 ) { // CRCIRq bit set - calculation done
break ;
}
}
PCD_WriteRegister ( CommandReg , PCD_Idle ) ; // Stop calculating CRC for new content in the FIFO.
// 7. Read out resulting 64 bytes from the FIFO buffer.
byte result [ 64 ] ;
PCD_ReadRegister ( FIFODataReg , 64 , result , 0 ) ;
// Auto self-test done
// Reset AutoTestReg register to be 0 again. Required for normal operation.
PCD_WriteRegister ( AutoTestReg , 0x00 ) ;
// Determine firmware version (see section 9.3.4.8 in spec)
byte version = PCD_ReadRegister ( VersionReg ) ;
// Pick the appropriate reference values
const byte * reference ;
switch ( version ) {
case 0x88 : // Fudan Semiconductor FM17522 clone
reference = FM17522_firmware_reference ;
break ;
case 0x90 : // Version 0.0
reference = MFRC522_firmware_referenceV0_0 ;
break ;
case 0x91 : // Version 1.0
reference = MFRC522_firmware_referenceV1_0 ;
break ;
case 0x92 : // Version 2.0
reference = MFRC522_firmware_referenceV2_0 ;
break ;
default : // Unknown version
return false ;
}
// Verify that the results match up to our expectations
for ( i = 0 ; i < 64 ; i + + ) {
if ( result [ i ] ! = pgm_read_byte ( & ( reference [ i ] ) ) ) {
return false ;
}
}
// Test passed; all is good.
return true ;
} // End PCD_PerformSelfTest()
/////////////////////////////////////////////////////////////////////////////////////
// Functions for communicating with PICCs
/////////////////////////////////////////////////////////////////////////////////////
/**
* Executes the Transceive command .
* CRC validation can only be done if backData and backLen are specified .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PCD_TransceiveData ( byte * sendData , ///< Pointer to the data to transfer to the FIFO.
byte sendLen , ///< Number of bytes to transfer to the FIFO.
byte * backData , ///< NULL or pointer to buffer if data should be read back after executing the command.
byte * backLen , ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned.
byte * validBits , ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL.
byte rxAlign , ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated.
) {
byte waitIRq = 0x30 ; // RxIRq and IdleIRq
return PCD_CommunicateWithPICC ( PCD_Transceive , waitIRq , sendData , sendLen , backData , backLen , validBits , rxAlign , checkCRC ) ;
} // End PCD_TransceiveData()
/**
* Transfers data to the MFRC522 FIFO , executes a command , waits for completion and transfers data back from the FIFO .
* CRC validation can only be done if backData and backLen are specified .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PCD_CommunicateWithPICC ( byte command , ///< The command to execute. One of the PCD_Command enums.
byte waitIRq , ///< The bits in the ComIrqReg register that signals successful completion of the command.
byte * sendData , ///< Pointer to the data to transfer to the FIFO.
byte sendLen , ///< Number of bytes to transfer to the FIFO.
byte * backData , ///< NULL or pointer to buffer if data should be read back after executing the command.
byte * backLen , ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned.
byte * validBits , ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits.
byte rxAlign , ///< In: Defines the bit position in backData[0] for the first bit received. Default 0.
bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated.
) {
byte n , _validBits ;
unsigned int i ;
// Prepare values for BitFramingReg
byte txLastBits = validBits ? * validBits : 0 ;
byte bitFraming = ( rxAlign < < 4 ) + txLastBits ; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
PCD_WriteRegister ( CommandReg , PCD_Idle ) ; // Stop any active command.
PCD_WriteRegister ( ComIrqReg , 0x7F ) ; // Clear all seven interrupt request bits
PCD_SetRegisterBitMask ( FIFOLevelReg , 0x80 ) ; // FlushBuffer = 1, FIFO initialization
PCD_WriteRegister ( FIFODataReg , sendLen , sendData ) ; // Write sendData to the FIFO
PCD_WriteRegister ( BitFramingReg , bitFraming ) ; // Bit adjustments
PCD_WriteRegister ( CommandReg , command ) ; // Execute the command
if ( command = = PCD_Transceive ) {
PCD_SetRegisterBitMask ( BitFramingReg , 0x80 ) ; // StartSend=1, transmission of data starts
}
// Wait for the command to complete.
// In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting.
// Each iteration of the do-while-loop takes 17.86<EFBFBD> s.
i = 2000 ;
while ( 1 ) {
n = PCD_ReadRegister ( ComIrqReg ) ; // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
if ( n & waitIRq ) { // One of the interrupts that signal success has been set.
break ;
}
if ( n & 0x01 ) { // Timer interrupt - nothing received in 25ms
return STATUS_TIMEOUT ;
}
if ( - - i = = 0 ) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down.
return STATUS_TIMEOUT ;
}
}
// Stop now if any errors except collisions were detected.
byte errorRegValue = PCD_ReadRegister ( ErrorReg ) ; // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
if ( errorRegValue & 0x13 ) { // BufferOvfl ParityErr ProtocolErr
return STATUS_ERROR ;
}
// If the caller wants data back, get it from the MFRC522.
if ( backData & & backLen ) {
n = PCD_ReadRegister ( FIFOLevelReg ) ; // Number of bytes in the FIFO
if ( n > * backLen ) {
return STATUS_NO_ROOM ;
}
* backLen = n ; // Number of bytes returned
PCD_ReadRegister ( FIFODataReg , n , backData , rxAlign ) ; // Get received data from FIFO
_validBits = PCD_ReadRegister ( ControlReg ) & 0x07 ; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid.
if ( validBits ) {
* validBits = _validBits ;
}
}
// Tell about collisions
if ( errorRegValue & 0x08 ) { // CollErr
return STATUS_COLLISION ;
}
// Perform CRC_A validation if requested.
if ( backData & & backLen & & checkCRC ) {
// In this case a MIFARE Classic NAK is not OK.
if ( * backLen = = 1 & & _validBits = = 4 ) {
return STATUS_MIFARE_NACK ;
}
// We need at least the CRC_A value and all 8 bits of the last byte must be received.
if ( * backLen < 2 | | _validBits ! = 0 ) {
return STATUS_CRC_WRONG ;
}
// Verify CRC_A - do our own calculation and store the control in controlBuffer.
byte controlBuffer [ 2 ] ;
n = PCD_CalculateCRC ( & backData [ 0 ] , * backLen - 2 , & controlBuffer [ 0 ] ) ;
if ( n ! = STATUS_OK ) {
return n ;
}
if ( ( backData [ * backLen - 2 ] ! = controlBuffer [ 0 ] ) | | ( backData [ * backLen - 1 ] ! = controlBuffer [ 1 ] ) ) {
return STATUS_CRC_WRONG ;
}
}
return STATUS_OK ;
} // End PCD_CommunicateWithPICC()
/**
* Transmits a REQuest command , Type A . Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection . 7 bit frame .
* Beware : When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PICC_RequestA ( byte * bufferATQA , ///< The buffer to store the ATQA (Answer to request) in
byte * bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
) {
return PICC_REQA_or_WUPA ( PICC_CMD_REQA , bufferATQA , bufferSize ) ;
} // End PICC_RequestA()
/**
* Transmits a Wake - UP command , Type A . Invites PICCs in state IDLE and HALT to go to READY ( * ) and prepare for anticollision or selection . 7 bit frame .
* Beware : When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PICC_WakeupA ( byte * bufferATQA , ///< The buffer to store the ATQA (Answer to request) in
byte * bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
) {
return PICC_REQA_or_WUPA ( PICC_CMD_WUPA , bufferATQA , bufferSize ) ;
} // End PICC_WakeupA()
/**
* Transmits REQA or WUPA commands .
* Beware : When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PICC_REQA_or_WUPA ( byte command , ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
byte * bufferATQA , ///< The buffer to store the ATQA (Answer to request) in
byte * bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
) {
byte validBits ;
byte status ;
if ( bufferATQA = = NULL | | * bufferSize < 2 ) { // The ATQA response is 2 bytes long.
return STATUS_NO_ROOM ;
}
PCD_ClearRegisterBitMask ( CollReg , 0x80 ) ; // ValuesAfterColl=1 => Bits received after collision are cleared.
validBits = 7 ; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0]
status = PCD_TransceiveData ( & command , 1 , bufferATQA , bufferSize , & validBits ) ;
if ( status ! = STATUS_OK ) {
return status ;
}
if ( * bufferSize ! = 2 | | validBits ! = 0 ) { // ATQA must be exactly 16 bits.
return STATUS_ERROR ;
}
return STATUS_OK ;
} // End PICC_REQA_or_WUPA()
/**
* Transmits SELECT / ANTICOLLISION commands to select a single PICC .
* Before calling this function the PICCs must be placed in the READY ( * ) state by calling PICC_RequestA ( ) or PICC_WakeupA ( ) .
* On success :
* - The chosen PICC is in state ACTIVE ( * ) and all other PICCs have returned to state IDLE / HALT . ( Figure 7 of the ISO / IEC 14443 - 3 draft . )
* - The UID size and value of the chosen PICC is returned in * uid along with the SAK .
*
* A PICC UID consists of 4 , 7 or 10 bytes .
* Only 4 bytes can be specified in a SELECT command , so for the longer UIDs two or three iterations are used :
* UID size Number of UID bytes Cascade levels Example of PICC
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* single 4 1 MIFARE Classic
* double 7 2 MIFARE Ultralight
* triple 10 3 Not currently in use ?
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PICC_Select ( Uid * uid , ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size.
) {
bool uidComplete ;
bool selectDone ;
bool useCascadeTag ;
byte cascadeLevel = 1 ;
byte result ;
byte count ;
byte index ;
byte uidIndex ; // The first index in uid->uidByte[] that is used in the current Cascade Level.
int8_t currentLevelKnownBits ; // The number of known UID bits in the current Cascade Level.
byte buffer [ 9 ] ; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A
byte bufferUsed ; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO.
byte rxAlign ; // Used in BitFramingReg. Defines the bit position for the first bit received.
byte txLastBits ; // Used in BitFramingReg. The number of valid bits in the last transmitted byte.
byte * responseBuffer ;
byte responseLength ;
// Description of buffer structure:
// Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
// Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits.
// Byte 2: UID-data or CT See explanation below. CT means Cascade Tag.
// Byte 3: UID-data
// Byte 4: UID-data
// Byte 5: UID-data
// Byte 6: BCC Block Check Character - XOR of bytes 2-5
// Byte 7: CRC_A
// Byte 8: CRC_A
// The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level.
//
// Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
// UID size Cascade level Byte2 Byte3 Byte4 Byte5
// ======== ============= ===== ===== ===== =====
// 4 bytes 1 uid0 uid1 uid2 uid3
// 7 bytes 1 CT uid0 uid1 uid2
// 2 uid3 uid4 uid5 uid6
// 10 bytes 1 CT uid0 uid1 uid2
// 2 CT uid3 uid4 uid5
// 3 uid6 uid7 uid8 uid9
// Sanity checks
if ( validBits > 80 ) {
return STATUS_INVALID ;
}
// Prepare MFRC522
PCD_ClearRegisterBitMask ( CollReg , 0x80 ) ; // ValuesAfterColl=1 => Bits received after collision are cleared.
// Repeat Cascade Level loop until we have a complete UID.
uidComplete = false ;
while ( ! uidComplete ) {
// Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2.
switch ( cascadeLevel ) {
case 1 :
buffer [ 0 ] = PICC_CMD_SEL_CL1 ;
uidIndex = 0 ;
useCascadeTag = validBits & & uid - > size > 4 ; // When we know that the UID has more than 4 bytes
break ;
case 2 :
buffer [ 0 ] = PICC_CMD_SEL_CL2 ;
uidIndex = 3 ;
useCascadeTag = validBits & & uid - > size > 7 ; // When we know that the UID has more than 7 bytes
break ;
case 3 :
buffer [ 0 ] = PICC_CMD_SEL_CL3 ;
uidIndex = 6 ;
useCascadeTag = false ; // Never used in CL3.
break ;
default :
return STATUS_INTERNAL_ERROR ;
break ;
}
// How many UID bits are known in this Cascade Level?
currentLevelKnownBits = validBits - ( 8 * uidIndex ) ;
if ( currentLevelKnownBits < 0 ) {
currentLevelKnownBits = 0 ;
}
// Copy the known bits from uid->uidByte[] to buffer[]
index = 2 ; // destination index in buffer[]
if ( useCascadeTag ) {
buffer [ index + + ] = PICC_CMD_CT ;
}
byte bytesToCopy = currentLevelKnownBits / 8 + ( currentLevelKnownBits % 8 ? 1 : 0 ) ; // The number of bytes needed to represent the known bits for this level.
if ( bytesToCopy ) {
byte maxBytes = useCascadeTag ? 3 : 4 ; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag
if ( bytesToCopy > maxBytes ) {
bytesToCopy = maxBytes ;
}
for ( count = 0 ; count < bytesToCopy ; count + + ) {
buffer [ index + + ] = uid - > uidByte [ uidIndex + count ] ;
}
}
// Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
if ( useCascadeTag ) {
currentLevelKnownBits + = 8 ;
}
// Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
selectDone = false ;
while ( ! selectDone ) {
// Find out how many bits and bytes to send and receive.
if ( currentLevelKnownBits > = 32 ) { // All UID bits in this Cascade Level are known. This is a SELECT.
//Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
buffer [ 1 ] = 0x70 ; // NVB - Number of Valid Bits: Seven whole bytes
// Calculate BCC - Block Check Character
buffer [ 6 ] = buffer [ 2 ] ^ buffer [ 3 ] ^ buffer [ 4 ] ^ buffer [ 5 ] ;
// Calculate CRC_A
result = PCD_CalculateCRC ( buffer , 7 , & buffer [ 7 ] ) ;
if ( result ! = STATUS_OK ) {
return result ;
}
txLastBits = 0 ; // 0 => All 8 bits are valid.
bufferUsed = 9 ;
// Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx)
responseBuffer = & buffer [ 6 ] ;
responseLength = 3 ;
}
else { // This is an ANTICOLLISION.
//Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC);
txLastBits = currentLevelKnownBits % 8 ;
count = currentLevelKnownBits / 8 ; // Number of whole bytes in the UID part.
index = 2 + count ; // Number of whole bytes: SEL + NVB + UIDs
buffer [ 1 ] = ( index < < 4 ) + txLastBits ; // NVB - Number of Valid Bits
bufferUsed = index + ( txLastBits ? 1 : 0 ) ;
// Store response in the unused part of buffer
responseBuffer = & buffer [ index ] ;
responseLength = sizeof ( buffer ) - index ;
}
// Set bit adjustments
rxAlign = txLastBits ; // Having a seperate variable is overkill. But it makes the next line easier to read.
PCD_WriteRegister ( BitFramingReg , ( rxAlign < < 4 ) + txLastBits ) ; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
// Transmit the buffer and receive the response.
result = PCD_TransceiveData ( buffer , bufferUsed , responseBuffer , & responseLength , & txLastBits , rxAlign ) ;
if ( result = = STATUS_COLLISION ) { // More than one PICC in the field => collision.
result = PCD_ReadRegister ( CollReg ) ; // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
if ( result & 0x20 ) { // CollPosNotValid
return STATUS_COLLISION ; // Without a valid collision position we cannot continue
}
byte collisionPos = result & 0x1F ; // Values 0-31, 0 means bit 32.
if ( collisionPos = = 0 ) {
collisionPos = 32 ;
}
if ( collisionPos < = currentLevelKnownBits ) { // No progress - should not happen
return STATUS_INTERNAL_ERROR ;
}
// Choose the PICC with the bit set.
currentLevelKnownBits = collisionPos ;
count = ( currentLevelKnownBits - 1 ) % 8 ; // The bit to modify
index = 1 + ( currentLevelKnownBits / 8 ) + ( count ? 1 : 0 ) ; // First byte is index 0.
buffer [ index ] | = ( 1 < < count ) ;
}
else if ( result ! = STATUS_OK ) {
return result ;
}
else { // STATUS_OK
if ( currentLevelKnownBits > = 32 ) { // This was a SELECT.
selectDone = true ; // No more anticollision
// We continue below outside the while.
}
else { // This was an ANTICOLLISION.
// We now have all 32 bits of the UID in this Cascade Level
currentLevelKnownBits = 32 ;
// Run loop again to do the SELECT.
}
}
} // End of while (!selectDone)
// We do not check the CBB - it was constructed by us above.
// Copy the found UID bytes from buffer[] to uid->uidByte[]
index = ( buffer [ 2 ] = = PICC_CMD_CT ) ? 3 : 2 ; // source index in buffer[]
bytesToCopy = ( buffer [ 2 ] = = PICC_CMD_CT ) ? 3 : 4 ;
for ( count = 0 ; count < bytesToCopy ; count + + ) {
uid - > uidByte [ uidIndex + count ] = buffer [ index + + ] ;
}
// Check response SAK (Select Acknowledge)
if ( responseLength ! = 3 | | txLastBits ! = 0 ) { // SAK must be exactly 24 bits (1 byte + CRC_A).
return STATUS_ERROR ;
}
// Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore.
result = PCD_CalculateCRC ( responseBuffer , 1 , & buffer [ 2 ] ) ;
if ( result ! = STATUS_OK ) {
return result ;
}
if ( ( buffer [ 2 ] ! = responseBuffer [ 1 ] ) | | ( buffer [ 3 ] ! = responseBuffer [ 2 ] ) ) {
return STATUS_CRC_WRONG ;
}
if ( responseBuffer [ 0 ] & 0x04 ) { // Cascade bit set - UID not complete yes
cascadeLevel + + ;
}
else {
uidComplete = true ;
uid - > sak = responseBuffer [ 0 ] ;
}
} // End of while (!uidComplete)
// Set correct uid->size
uid - > size = 3 * cascadeLevel + 1 ;
return STATUS_OK ;
} // End PICC_Select()
/**
* Instructs a PICC in state ACTIVE ( * ) to go to state HALT .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PICC_HaltA ( ) {
byte result ;
byte buffer [ 4 ] ;
// Build command buffer
buffer [ 0 ] = PICC_CMD_HLTA ;
buffer [ 1 ] = 0 ;
// Calculate CRC_A
result = PCD_CalculateCRC ( buffer , 2 , & buffer [ 2 ] ) ;
if ( result ! = STATUS_OK ) {
return result ;
}
// Send the command.
// The standard says:
// If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the
// HLTA command, this response shall be interpreted as 'not acknowledge'.
// We interpret that this way: Only STATUS_TIMEOUT is an success.
result = PCD_TransceiveData ( buffer , sizeof ( buffer ) , NULL , 0 ) ;
if ( result = = STATUS_TIMEOUT ) {
return STATUS_OK ;
}
if ( result = = STATUS_OK ) { // That is ironically NOT ok in this case ;-)
return STATUS_ERROR ;
}
return result ;
} // End PICC_HaltA()
/////////////////////////////////////////////////////////////////////////////////////
// Functions for communicating with MIFARE PICCs
/////////////////////////////////////////////////////////////////////////////////////
/**
* Executes the MFRC522 MFAuthent command .
* This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini , MIFARE 1 K and MIFARE 4 K card .
* The authentication is described in the MFRC522 datasheet section 10.3 .1 .9 and http : //www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1.
* For use with MIFARE Classic PICCs .
* The PICC must be selected - ie in state ACTIVE ( * ) - before calling this function .
* Remember to call PCD_StopCrypto1 ( ) after communicating with the authenticated PICC - otherwise no new communications can start .
*
* All keys are set to FFFFFFFFFFFFh at chip delivery .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise . Probably STATUS_TIMEOUT if you supply the wrong key .
*/
byte MFRC522 : : PCD_Authenticate ( byte command , ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B
byte blockAddr , ///< The block number. See numbering in the comments in the .h file.
MIFARE_Key * key , ///< Pointer to the Crypteo1 key to use (6 bytes)
Uid * uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used.
) {
byte waitIRq = 0x10 ; // IdleIRq
// Build command buffer
byte sendData [ 12 ] ;
sendData [ 0 ] = command ;
sendData [ 1 ] = blockAddr ;
for ( byte i = 0 ; i < MF_KEY_SIZE ; i + + ) { // 6 key bytes
sendData [ 2 + i ] = key - > keyByte [ i ] ;
}
for ( byte i = 0 ; i < 4 ; i + + ) { // The first 4 bytes of the UID
sendData [ 8 + i ] = uid - > uidByte [ i ] ;
}
// Start the authentication.
return PCD_CommunicateWithPICC ( PCD_MFAuthent , waitIRq , & sendData [ 0 ] , sizeof ( sendData ) ) ;
} // End PCD_Authenticate()
/**
* Used to exit the PCD from its authenticated state .
* Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start .
*/
void MFRC522 : : PCD_StopCrypto1 ( ) {
// Clear MFCrypto1On bit
PCD_ClearRegisterBitMask ( Status2Reg , 0x08 ) ; // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0]
} // End PCD_StopCrypto1()
/**
* Reads 16 bytes ( + 2 bytes CRC_A ) from the active PICC .
*
* For MIFARE Classic the sector containing the block must be authenticated before calling this function .
*
* For MIFARE Ultralight only addresses 00 h to 0F h are decoded .
* The MF0ICU1 returns a NAK for higher addresses .
* The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument .
* For example ; if blockAddr is 03 h then pages 03 h , 04 h , 05 h , 06 h are returned .
* A roll - back is implemented : If blockAddr is 0 Eh , then the contents of pages 0 Eh , 0F h , 00 h and 01 h are returned .
*
* The buffer must be at least 18 bytes because a CRC_A is also returned .
* Checks the CRC_A before returning STATUS_OK .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Read ( byte blockAddr , ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from.
byte * buffer , ///< The buffer to store the data in
byte * bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK.
) {
byte result ;
// Sanity check
if ( buffer = = NULL | | * bufferSize < 18 ) {
return STATUS_NO_ROOM ;
}
// Build command buffer
buffer [ 0 ] = PICC_CMD_MF_READ ;
buffer [ 1 ] = blockAddr ;
// Calculate CRC_A
result = PCD_CalculateCRC ( buffer , 2 , & buffer [ 2 ] ) ;
if ( result ! = STATUS_OK ) {
return result ;
}
// Transmit the buffer and receive the response, validate CRC_A.
return PCD_TransceiveData ( buffer , 4 , buffer , bufferSize , NULL , 0 , true ) ;
} // End MIFARE_Read()
/**
* Writes 16 bytes to the active PICC .
*
* For MIFARE Classic the sector containing the block must be authenticated before calling this function .
*
* For MIFARE Ultralight the operation is called " COMPATIBILITY WRITE " .
* Even though 16 bytes are transferred to the Ultralight PICC , only the least significant 4 bytes ( bytes 0 to 3 )
* are written to the specified address . It is recommended to set the remaining bytes 04 h to 0F h to all logic 0.
* *
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Write ( byte blockAddr , ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to.
byte * buffer , ///< The 16 bytes to write to the PICC
byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written.
) {
byte result ;
// Sanity check
if ( buffer = = NULL | | bufferSize < 16 ) {
return STATUS_INVALID ;
}
// Mifare Classic protocol requires two communications to perform a write.
// Step 1: Tell the PICC we want to write to block blockAddr.
byte cmdBuffer [ 2 ] ;
cmdBuffer [ 0 ] = PICC_CMD_MF_WRITE ;
cmdBuffer [ 1 ] = blockAddr ;
result = PCD_MIFARE_Transceive ( cmdBuffer , 2 ) ; // Adds CRC_A and checks that the response is MF_ACK.
if ( result ! = STATUS_OK ) {
return result ;
}
// Step 2: Transfer the data
result = PCD_MIFARE_Transceive ( buffer , bufferSize ) ; // Adds CRC_A and checks that the response is MF_ACK.
if ( result ! = STATUS_OK ) {
return result ;
}
return STATUS_OK ;
} // End MIFARE_Write()
/**
* Writes a 4 byte page to the active MIFARE Ultralight PICC .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Ultralight_Write ( byte page , ///< The page (2-15) to write to.
byte * buffer , ///< The 4 bytes to write to the PICC
byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written.
) {
byte result ;
// Sanity check
if ( buffer = = NULL | | bufferSize < 4 ) {
return STATUS_INVALID ;
}
// Build commmand buffer
byte cmdBuffer [ 6 ] ;
cmdBuffer [ 0 ] = PICC_CMD_UL_WRITE ;
cmdBuffer [ 1 ] = page ;
memcpy ( & cmdBuffer [ 2 ] , buffer , 4 ) ;
// Perform the write
result = PCD_MIFARE_Transceive ( cmdBuffer , 6 ) ; // Adds CRC_A and checks that the response is MF_ACK.
if ( result ! = STATUS_OK ) {
return result ;
}
return STATUS_OK ;
} // End MIFARE_Ultralight_Write()
/**
* MIFARE Decrement subtracts the delta from the value of the addressed block , and stores the result in a volatile memory .
* For MIFARE Classic only . The sector containing the block must be authenticated before calling this function .
* Only for blocks in " value block " mode , ie with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] .
* Use MIFARE_Transfer ( ) to store the result in a block .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Decrement ( byte blockAddr , ///< The block (0-0xff) number.
long delta ///< This number is subtracted from the value of block blockAddr.
) {
return MIFARE_TwoStepHelper ( PICC_CMD_MF_DECREMENT , blockAddr , delta ) ;
} // End MIFARE_Decrement()
/**
* MIFARE Increment adds the delta to the value of the addressed block , and stores the result in a volatile memory .
* For MIFARE Classic only . The sector containing the block must be authenticated before calling this function .
* Only for blocks in " value block " mode , ie with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] .
* Use MIFARE_Transfer ( ) to store the result in a block .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Increment ( byte blockAddr , ///< The block (0-0xff) number.
long delta ///< This number is added to the value of block blockAddr.
) {
return MIFARE_TwoStepHelper ( PICC_CMD_MF_INCREMENT , blockAddr , delta ) ;
} // End MIFARE_Increment()
/**
* MIFARE Restore copies the value of the addressed block into a volatile memory .
* For MIFARE Classic only . The sector containing the block must be authenticated before calling this function .
* Only for blocks in " value block " mode , ie with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] .
* Use MIFARE_Transfer ( ) to store the result in a block .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Restore ( byte blockAddr ///< The block (0-0xff) number.
) {
// The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2.
// Doing only a single step does not work, so I chose to transfer 0L in step two.
return MIFARE_TwoStepHelper ( PICC_CMD_MF_RESTORE , blockAddr , 0L ) ;
} // End MIFARE_Restore()
/**
* Helper function for the two - step MIFARE Classic protocol operations Decrement , Increment and Restore .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_TwoStepHelper ( byte command , ///< The command to use
byte blockAddr , ///< The block (0-0xff) number.
long data ///< The data to transfer in step 2
) {
byte result ;
byte cmdBuffer [ 2 ] ; // We only need room for 2 bytes.
// Step 1: Tell the PICC the command and block address
cmdBuffer [ 0 ] = command ;
cmdBuffer [ 1 ] = blockAddr ;
result = PCD_MIFARE_Transceive ( cmdBuffer , 2 ) ; // Adds CRC_A and checks that the response is MF_ACK.
if ( result ! = STATUS_OK ) {
return result ;
}
// Step 2: Transfer the data
result = PCD_MIFARE_Transceive ( ( byte * ) & data , 4 , true ) ; // Adds CRC_A and accept timeout as success.
if ( result ! = STATUS_OK ) {
return result ;
}
return STATUS_OK ;
} // End MIFARE_TwoStepHelper()
/**
* MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block .
* For MIFARE Classic only . The sector containing the block must be authenticated before calling this function .
* Only for blocks in " value block " mode , ie with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_Transfer ( byte blockAddr ///< The block (0-0xff) number.
) {
byte result ;
byte cmdBuffer [ 2 ] ; // We only need room for 2 bytes.
// Tell the PICC we want to transfer the result into block blockAddr.
cmdBuffer [ 0 ] = PICC_CMD_MF_TRANSFER ;
cmdBuffer [ 1 ] = blockAddr ;
result = PCD_MIFARE_Transceive ( cmdBuffer , 2 ) ; // Adds CRC_A and checks that the response is MF_ACK.
if ( result ! = STATUS_OK ) {
return result ;
}
return STATUS_OK ;
} // End MIFARE_Transfer()
/**
* Helper routine to read the current value from a Value Block .
*
* Only for MIFARE Classic and only for blocks in " value block " mode , that
* is : with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] . The sector containing
* the block must be authenticated before calling this function .
*
* @ param [ in ] blockAddr The block ( 0x00 - 0xff ) number .
* @ param [ out ] value Current value of the Value Block .
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_GetValue ( byte blockAddr , long * value ) {
byte status ;
byte buffer [ 18 ] ;
byte size = sizeof ( buffer ) ;
// Read the block
status = MIFARE_Read ( blockAddr , buffer , & size ) ;
if ( status = = STATUS_OK ) {
// Extract the value
* value = ( long ( buffer [ 3 ] ) < < 24 ) | ( long ( buffer [ 2 ] ) < < 16 ) | ( long ( buffer [ 1 ] ) < < 8 ) | long ( buffer [ 0 ] ) ;
}
return status ;
} // End MIFARE_GetValue()
/**
* Helper routine to write a specific value into a Value Block .
*
* Only for MIFARE Classic and only for blocks in " value block " mode , that
* is : with access bits [ C1 C2 C3 ] = [ 110 ] or [ 001 ] . The sector containing
* the block must be authenticated before calling this function .
*
* @ param [ in ] blockAddr The block ( 0x00 - 0xff ) number .
* @ param [ in ] value New value of the Value Block .
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : MIFARE_SetValue ( byte blockAddr , long value ) {
byte buffer [ 18 ] ;
// Translate the long into 4 bytes; repeated 2x in value block
buffer [ 0 ] = buffer [ 8 ] = ( value & 0xFF ) ;
buffer [ 1 ] = buffer [ 9 ] = ( value & 0xFF00 ) > > 8 ;
buffer [ 2 ] = buffer [ 10 ] = ( value & 0xFF0000 ) > > 16 ;
buffer [ 3 ] = buffer [ 11 ] = ( value & 0xFF000000 ) > > 24 ;
// Inverse 4 bytes also found in value block
buffer [ 4 ] = ~ buffer [ 0 ] ;
buffer [ 5 ] = ~ buffer [ 1 ] ;
buffer [ 6 ] = ~ buffer [ 2 ] ;
buffer [ 7 ] = ~ buffer [ 3 ] ;
// Address 2x with inverse address 2x
buffer [ 12 ] = buffer [ 14 ] = blockAddr ;
buffer [ 13 ] = buffer [ 15 ] = ~ blockAddr ;
// Write the whole data block
return MIFARE_Write ( blockAddr , buffer , 16 ) ;
} // End MIFARE_SetValue()
/////////////////////////////////////////////////////////////////////////////////////
// Support functions
/////////////////////////////////////////////////////////////////////////////////////
/**
* Wrapper for MIFARE protocol communication .
* Adds CRC_A , executes the Transceive command and checks that the response is MF_ACK or a timeout .
*
* @ return STATUS_OK on success , STATUS_ ? ? ? otherwise .
*/
byte MFRC522 : : PCD_MIFARE_Transceive ( byte * sendData , ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A.
byte sendLen , ///< Number of bytes in sendData.
bool acceptTimeout ///< True => A timeout is also success
) {
byte result ;
byte cmdBuffer [ 18 ] ; // We need room for 16 bytes data and 2 bytes CRC_A.
// Sanity check
if ( sendData = = NULL | | sendLen > 16 ) {
return STATUS_INVALID ;
}
// Copy sendData[] to cmdBuffer[] and add CRC_A
memcpy ( cmdBuffer , sendData , sendLen ) ;
result = PCD_CalculateCRC ( cmdBuffer , sendLen , & cmdBuffer [ sendLen ] ) ;
if ( result ! = STATUS_OK ) {
return result ;
}
sendLen + = 2 ;
// Transceive the data, store the reply in cmdBuffer[]
byte waitIRq = 0x30 ; // RxIRq and IdleIRq
byte cmdBufferSize = sizeof ( cmdBuffer ) ;
byte validBits = 0 ;
result = PCD_CommunicateWithPICC ( PCD_Transceive , waitIRq , cmdBuffer , sendLen , cmdBuffer , & cmdBufferSize , & validBits ) ;
if ( acceptTimeout & & result = = STATUS_TIMEOUT ) {
return STATUS_OK ;
}
if ( result ! = STATUS_OK ) {
return result ;
}
// The PICC must reply with a 4 bit ACK
if ( cmdBufferSize ! = 1 | | validBits ! = 4 ) {
return STATUS_ERROR ;
}
if ( cmdBuffer [ 0 ] ! = MF_ACK ) {
return STATUS_MIFARE_NACK ;
}
return STATUS_OK ;
} // End PCD_MIFARE_Transceive()
/**
* Returns a __FlashStringHelper pointer to a status code name .
*
* @ return const __FlashStringHelper *
*/
const __FlashStringHelper * MFRC522 : : GetStatusCodeName ( byte code ///< One of the StatusCode enums.
) {
switch ( code ) {
case STATUS_OK : return F ( " Success. " ) ; break ;
case STATUS_ERROR : return F ( " Error in communication. " ) ; break ;
case STATUS_COLLISION : return F ( " Collission detected. " ) ; break ;
case STATUS_TIMEOUT : return F ( " Timeout in communication. " ) ; break ;
case STATUS_NO_ROOM : return F ( " A buffer is not big enough. " ) ; break ;
case STATUS_INTERNAL_ERROR : return F ( " Internal error in the code. Should not happen. " ) ; break ;
case STATUS_INVALID : return F ( " Invalid argument. " ) ; break ;
case STATUS_CRC_WRONG : return F ( " The CRC_A does not match. " ) ; break ;
case STATUS_MIFARE_NACK : return F ( " A MIFARE PICC responded with NAK. " ) ; break ;
default : return F ( " Unknown error " ) ; break ;
}
} // End GetStatusCodeName()
/**
* Translates the SAK ( Select Acknowledge ) to a PICC type .
*
* @ return PICC_Type
*/
byte MFRC522 : : PICC_GetType ( byte sak ///< The SAK byte returned from PICC_Select().
) {
if ( sak & 0x04 ) { // UID not complete
return PICC_TYPE_NOT_COMPLETE ;
}
switch ( sak ) {
case 0x09 : return PICC_TYPE_MIFARE_MINI ; break ;
case 0x08 : return PICC_TYPE_MIFARE_1K ; break ;
case 0x18 : return PICC_TYPE_MIFARE_4K ; break ;
case 0x00 : return PICC_TYPE_MIFARE_UL ; break ;
case 0x10 :
case 0x11 : return PICC_TYPE_MIFARE_PLUS ; break ;
case 0x01 : return PICC_TYPE_TNP3XXX ; break ;
default : break ;
}
if ( sak & 0x20 ) {
return PICC_TYPE_ISO_14443_4 ;
}
if ( sak & 0x40 ) {
return PICC_TYPE_ISO_18092 ;
}
return PICC_TYPE_UNKNOWN ;
} // End PICC_GetType()
/**
* Returns a __FlashStringHelper pointer to the PICC type name .
*
* @ return const __FlashStringHelper *
*/
const __FlashStringHelper * MFRC522 : : PICC_GetTypeName ( byte piccType ///< One of the PICC_Type enums.
) {
switch ( piccType ) {
case PICC_TYPE_ISO_14443_4 : return F ( " PICC compliant with ISO/IEC 14443-4 " ) ; break ;
case PICC_TYPE_ISO_18092 : return F ( " PICC compliant with ISO/IEC 18092 (NFC) " ) ; break ;
case PICC_TYPE_MIFARE_MINI : return F ( " MIFARE Mini, 320 bytes " ) ; break ;
case PICC_TYPE_MIFARE_1K : return F ( " MIFARE 1KB " ) ; break ;
case PICC_TYPE_MIFARE_4K : return F ( " MIFARE 4KB " ) ; break ;
case PICC_TYPE_MIFARE_UL : return F ( " MIFARE Ultralight or Ultralight C " ) ; break ;
case PICC_TYPE_MIFARE_PLUS : return F ( " MIFARE Plus " ) ; break ;
case PICC_TYPE_TNP3XXX : return F ( " MIFARE TNP3XXX " ) ; break ;
case PICC_TYPE_NOT_COMPLETE : return F ( " SAK indicates UID is not complete. " ) ; break ;
case PICC_TYPE_UNKNOWN :
default : return F ( " Unknown type " ) ; break ;
}
} // End PICC_GetTypeName()
/**
* Dumps debug info about the selected PICC to Serial .
* On success the PICC is halted after dumping the data .
* For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried .
*/
void MFRC522 : : PICC_DumpToSerial ( Uid * uid ///< Pointer to Uid struct returned from a successful PICC_Select().
) {
MIFARE_Key key ;
// UID
Serial . print ( F ( " Card UID: " ) ) ;
for ( byte i = 0 ; i < uid - > size ; i + + ) {
if ( uid - > uidByte [ i ] < 0x10 )
Serial . print ( F ( " 0 " ) ) ;
else
Serial . print ( F ( " " ) ) ;
Serial . print ( uid - > uidByte [ i ] , HEX ) ;
}
Serial . println ( ) ;
// PICC type
byte piccType = PICC_GetType ( uid - > sak ) ;
Serial . print ( F ( " PICC type: " ) ) ;
Serial . println ( PICC_GetTypeName ( piccType ) ) ;
// Dump contents
switch ( piccType ) {
case PICC_TYPE_MIFARE_MINI :
case PICC_TYPE_MIFARE_1K :
case PICC_TYPE_MIFARE_4K :
// All keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
for ( byte i = 0 ; i < 6 ; i + + ) {
key . keyByte [ i ] = 0xFF ;
}
PICC_DumpMifareClassicToSerial ( uid , piccType , & key ) ;
break ;
case PICC_TYPE_MIFARE_UL :
PICC_DumpMifareUltralightToSerial ( ) ;
break ;
case PICC_TYPE_ISO_14443_4 :
case PICC_TYPE_ISO_18092 :
case PICC_TYPE_MIFARE_PLUS :
case PICC_TYPE_TNP3XXX :
Serial . println ( F ( " Dumping memory contents not implemented for that PICC type. " ) ) ;
break ;
case PICC_TYPE_UNKNOWN :
case PICC_TYPE_NOT_COMPLETE :
default :
break ; // No memory dump here
}
Serial . println ( ) ;
PICC_HaltA ( ) ; // Already done if it was a MIFARE Classic PICC.
} // End PICC_DumpToSerial()
/**
* Dumps memory contents of a MIFARE Classic PICC .
* On success the PICC is halted after dumping the data .
*/
void MFRC522 : : PICC_DumpMifareClassicToSerial ( Uid * uid , ///< Pointer to Uid struct returned from a successful PICC_Select().
byte piccType , ///< One of the PICC_Type enums.
MIFARE_Key * key ///< Key A used for all sectors.
) {
byte no_of_sectors = 0 ;
switch ( piccType ) {
case PICC_TYPE_MIFARE_MINI :
// Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes.
no_of_sectors = 5 ;
break ;
case PICC_TYPE_MIFARE_1K :
// Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes.
no_of_sectors = 16 ;
break ;
case PICC_TYPE_MIFARE_4K :
// Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes.
no_of_sectors = 40 ;
break ;
default : // Should not happen. Ignore.
break ;
}
// Dump sectors, highest address first.
if ( no_of_sectors ) {
Serial . println ( F ( " Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits " ) ) ;
for ( int8_t i = no_of_sectors - 1 ; i > = 0 ; i - - ) {
PICC_DumpMifareClassicSectorToSerial ( uid , key , i ) ;
}
}
PICC_HaltA ( ) ; // Halt the PICC before stopping the encrypted session.
PCD_StopCrypto1 ( ) ;
} // End PICC_DumpMifareClassicToSerial()
/**
* Dumps memory contents of a sector of a MIFARE Classic PICC .
* Uses PCD_Authenticate ( ) , MIFARE_Read ( ) and PCD_StopCrypto1 .
* Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits .
*/
void MFRC522 : : PICC_DumpMifareClassicSectorToSerial ( Uid * uid , ///< Pointer to Uid struct returned from a successful PICC_Select().
MIFARE_Key * key , ///< Key A for the sector.
byte sector ///< The sector to dump, 0..39.
) {
byte status ;
byte firstBlock ; // Address of lowest address to dump actually last block dumped)
byte no_of_blocks ; // Number of blocks in sector
bool isSectorTrailer ; // Set to true while handling the "last" (ie highest address) in the sector.
// The access bits are stored in a peculiar fashion.
// There are four groups:
// g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
// g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
// g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
// g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
// Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB.
// The four CX bits are stored together in a nible cx and an inverted nible cx_.
byte c1 , c2 , c3 ; // Nibbles
byte c1_ , c2_ , c3_ ; // Inverted nibbles
bool invertedError ; // True if one of the inverted nibbles did not match
byte g [ 4 ] ; // Access bits for each of the four groups.
byte group ; // 0-3 - active group for access bits
bool firstInGroup ; // True for the first block dumped in the group
// Determine position and size of sector.
if ( sector < 32 ) { // Sectors 0..31 has 4 blocks each
no_of_blocks = 4 ;
firstBlock = sector * no_of_blocks ;
}
else if ( sector < 40 ) { // Sectors 32-39 has 16 blocks each
no_of_blocks = 16 ;
firstBlock = 128 + ( sector - 32 ) * no_of_blocks ;
}
else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors.
return ;
}
// Dump blocks, highest address first.
byte byteCount ;
byte buffer [ 18 ] ;
byte blockAddr ;
isSectorTrailer = true ;
for ( int8_t blockOffset = no_of_blocks - 1 ; blockOffset > = 0 ; blockOffset - - ) {
blockAddr = firstBlock + blockOffset ;
// Sector number - only on first line
if ( isSectorTrailer ) {
if ( sector < 10 )
Serial . print ( F ( " " ) ) ; // Pad with spaces
else
Serial . print ( F ( " " ) ) ; // Pad with spaces
Serial . print ( sector ) ;
Serial . print ( F ( " " ) ) ;
}
else {
Serial . print ( F ( " " ) ) ;
}
// Block number
if ( blockAddr < 10 )
Serial . print ( F ( " " ) ) ; // Pad with spaces
else {
if ( blockAddr < 100 )
Serial . print ( F ( " " ) ) ; // Pad with spaces
else
Serial . print ( F ( " " ) ) ; // Pad with spaces
}
Serial . print ( blockAddr ) ;
Serial . print ( F ( " " ) ) ;
// Establish encrypted communications before reading the first block
if ( isSectorTrailer ) {
status = PCD_Authenticate ( PICC_CMD_MF_AUTH_KEY_A , firstBlock , key , uid ) ;
if ( status ! = STATUS_OK ) {
Serial . print ( F ( " PCD_Authenticate() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
return ;
}
}
// Read block
byteCount = sizeof ( buffer ) ;
status = MIFARE_Read ( blockAddr , buffer , & byteCount ) ;
if ( status ! = STATUS_OK ) {
Serial . print ( F ( " MIFARE_Read() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
continue ;
}
// Dump data
for ( byte index = 0 ; index < 16 ; index + + ) {
if ( buffer [ index ] < 0x10 )
Serial . print ( F ( " 0 " ) ) ;
else
Serial . print ( F ( " " ) ) ;
Serial . print ( buffer [ index ] , HEX ) ;
if ( ( index % 4 ) = = 3 ) {
Serial . print ( F ( " " ) ) ;
}
}
// Parse sector trailer data
if ( isSectorTrailer ) {
c1 = buffer [ 7 ] > > 4 ;
c2 = buffer [ 8 ] & 0xF ;
c3 = buffer [ 8 ] > > 4 ;
c1_ = buffer [ 6 ] & 0xF ;
c2_ = buffer [ 6 ] > > 4 ;
c3_ = buffer [ 7 ] & 0xF ;
invertedError = ( c1 ! = ( ~ c1_ & 0xF ) ) | | ( c2 ! = ( ~ c2_ & 0xF ) ) | | ( c3 ! = ( ~ c3_ & 0xF ) ) ;
g [ 0 ] = ( ( c1 & 1 ) < < 2 ) | ( ( c2 & 1 ) < < 1 ) | ( ( c3 & 1 ) < < 0 ) ;
g [ 1 ] = ( ( c1 & 2 ) < < 1 ) | ( ( c2 & 2 ) < < 0 ) | ( ( c3 & 2 ) > > 1 ) ;
g [ 2 ] = ( ( c1 & 4 ) < < 0 ) | ( ( c2 & 4 ) > > 1 ) | ( ( c3 & 4 ) > > 2 ) ;
g [ 3 ] = ( ( c1 & 8 ) > > 1 ) | ( ( c2 & 8 ) > > 2 ) | ( ( c3 & 8 ) > > 3 ) ;
isSectorTrailer = false ;
}
// Which access group is this block in?
if ( no_of_blocks = = 4 ) {
group = blockOffset ;
firstInGroup = true ;
}
else {
group = blockOffset / 5 ;
firstInGroup = ( group = = 3 ) | | ( group ! = ( blockOffset + 1 ) / 5 ) ;
}
if ( firstInGroup ) {
// Print access bits
Serial . print ( F ( " [ " ) ) ;
Serial . print ( ( g [ group ] > > 2 ) & 1 , DEC ) ; Serial . print ( F ( " " ) ) ;
Serial . print ( ( g [ group ] > > 1 ) & 1 , DEC ) ; Serial . print ( F ( " " ) ) ;
Serial . print ( ( g [ group ] > > 0 ) & 1 , DEC ) ;
Serial . print ( F ( " ] " ) ) ;
if ( invertedError ) {
Serial . print ( F ( " Inverted access bits did not match! " ) ) ;
}
}
if ( group ! = 3 & & ( g [ group ] = = 1 | | g [ group ] = = 6 ) ) { // Not a sector trailer, a value block
long value = ( long ( buffer [ 3 ] ) < < 24 ) | ( long ( buffer [ 2 ] ) < < 16 ) | ( long ( buffer [ 1 ] ) < < 8 ) | long ( buffer [ 0 ] ) ;
Serial . print ( F ( " Value=0x " ) ) ; Serial . print ( value , HEX ) ;
Serial . print ( F ( " Adr=0x " ) ) ; Serial . print ( buffer [ 12 ] , HEX ) ;
}
Serial . println ( ) ;
}
return ;
} // End PICC_DumpMifareClassicSectorToSerial()
/**
* Dumps memory contents of a MIFARE Ultralight PICC .
*/
void MFRC522 : : PICC_DumpMifareUltralightToSerial ( ) {
byte status ;
byte byteCount ;
byte buffer [ 18 ] ;
byte i ;
Serial . println ( F ( " Page 0 1 2 3 " ) ) ;
// Try the mpages of the original Ultralight. Ultralight C has more pages.
for ( byte page = 0 ; page < 16 ; page + = 4 ) { // Read returns data for 4 pages at a time.
// Read pages
byteCount = sizeof ( buffer ) ;
status = MIFARE_Read ( page , buffer , & byteCount ) ;
if ( status ! = STATUS_OK ) {
Serial . print ( F ( " MIFARE_Read() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
break ;
}
// Dump data
for ( byte offset = 0 ; offset < 4 ; offset + + ) {
i = page + offset ;
if ( i < 10 )
Serial . print ( F ( " " ) ) ; // Pad with spaces
else
Serial . print ( F ( " " ) ) ; // Pad with spaces
Serial . print ( i ) ;
Serial . print ( F ( " " ) ) ;
for ( byte index = 0 ; index < 4 ; index + + ) {
i = 4 * offset + index ;
if ( buffer [ i ] < 0x10 )
Serial . print ( F ( " 0 " ) ) ;
else
Serial . print ( F ( " " ) ) ;
Serial . print ( buffer [ i ] , HEX ) ;
}
Serial . println ( ) ;
}
}
} // End PICC_DumpMifareUltralightToSerial()
/**
* Calculates the bit pattern needed for the specified access bits . In the [ C1 C2 C3 ] tupples C1 is MSB ( = 4 ) and C3 is LSB ( = 1 ) .
*/
void MFRC522 : : MIFARE_SetAccessBits ( byte * accessBitBuffer , ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set.
byte g0 , ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
byte g1 , ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
byte g2 , ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
) {
byte c1 = ( ( g3 & 4 ) < < 1 ) | ( ( g2 & 4 ) < < 0 ) | ( ( g1 & 4 ) > > 1 ) | ( ( g0 & 4 ) > > 2 ) ;
byte c2 = ( ( g3 & 2 ) < < 2 ) | ( ( g2 & 2 ) < < 1 ) | ( ( g1 & 2 ) < < 0 ) | ( ( g0 & 2 ) > > 1 ) ;
byte c3 = ( ( g3 & 1 ) < < 3 ) | ( ( g2 & 1 ) < < 2 ) | ( ( g1 & 1 ) < < 1 ) | ( ( g0 & 1 ) < < 0 ) ;
accessBitBuffer [ 0 ] = ( ~ c2 & 0xF ) < < 4 | ( ~ c1 & 0xF ) ;
accessBitBuffer [ 1 ] = c1 < < 4 | ( ~ c3 & 0xF ) ;
accessBitBuffer [ 2 ] = c3 < < 4 | c2 ;
} // End MIFARE_SetAccessBits()
/**
* Performs the " magic sequence " needed to get Chinese UID changeable
* Mifare cards to allow writing to sector 0 , where the card UID is stored .
*
* Note that you do not need to have selected the card through REQA or WUPA ,
* this sequence works immediately when the card is in the reader vicinity .
* This means you can use this method even on " bricked " cards that your reader does
* not recognise anymore ( see MFRC522 : : MIFARE_UnbrickUidSector ) .
*
* Of course with non - bricked devices , you ' re free to select them before calling this function .
*/
bool MFRC522 : : MIFARE_OpenUidBackdoor ( bool logErrors ) {
// Magic sequence:
// > 50 00 57 CD (HALT + CRC)
// > 40 (7 bits only)
// < A (4 bits only)
// > 43
// < A (4 bits only)
// Then you can write to sector 0 without authenticating
PICC_HaltA ( ) ; // 50 00 57 CD
byte cmd = 0x40 ;
byte validBits = 7 ; /* Our command is only 7 bits. After receiving card response,
this will contain amount of valid response bits . */
byte response [ 32 ] ; // Card's response is written here
byte received ;
byte status = PCD_TransceiveData ( & cmd , ( byte ) 1 , response , & received , & validBits , ( byte ) 0 , false ) ; // 40
if ( status ! = STATUS_OK ) {
if ( logErrors ) {
Serial . println ( F ( " Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one? " ) ) ;
Serial . print ( F ( " Error name: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
if ( received ! = 1 | | response [ 0 ] ! = 0x0A ) {
if ( logErrors ) {
Serial . print ( F ( " Got bad response on backdoor 0x40 command: " ) ) ;
Serial . print ( response [ 0 ] , HEX ) ;
Serial . print ( F ( " ( " ) ) ;
Serial . print ( validBits ) ;
Serial . print ( F ( " valid bits) \r \n " ) ) ;
}
return false ;
}
cmd = 0x43 ;
validBits = 8 ;
status = PCD_TransceiveData ( & cmd , ( byte ) 1 , response , & received , & validBits , ( byte ) 0 , false ) ; // 43
if ( status ! = STATUS_OK ) {
if ( logErrors ) {
Serial . println ( F ( " Error in communication at command 0x43, after successfully executing 0x40 " ) ) ;
Serial . print ( F ( " Error name: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
if ( received ! = 1 | | response [ 0 ] ! = 0x0A ) {
if ( logErrors ) {
Serial . print ( F ( " Got bad response on backdoor 0x43 command: " ) ) ;
Serial . print ( response [ 0 ] , HEX ) ;
Serial . print ( F ( " ( " ) ) ;
Serial . print ( validBits ) ;
Serial . print ( F ( " valid bits) \r \n " ) ) ;
}
return false ;
}
// You can now write to sector 0 without authenticating!
return true ;
} // End MIFARE_OpenUidBackdoor()
/**
* Reads entire block 0 , including all manufacturer data , and overwrites
* that block with the new UID , a freshly calculated BCC , and the original
* manufacturer data .
*
* It assumes a default KEY A of 0xFFFFFFFFFFFF .
* Make sure to have selected the card before this function is called .
*/
bool MFRC522 : : MIFARE_SetUid ( byte * newUid , byte uidSize , bool logErrors ) {
// UID + BCC byte can not be larger than 16 together
if ( ! newUid | | ! uidSize | | uidSize > 15 ) {
if ( logErrors ) {
Serial . println ( F ( " New UID buffer empty, size 0, or size > 15 given " ) ) ;
}
return false ;
}
// Authenticate for reading
MIFARE_Key key = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
byte status = PCD_Authenticate ( MFRC522 : : PICC_CMD_MF_AUTH_KEY_A , ( byte ) 1 , & key , & uid ) ;
if ( status ! = STATUS_OK ) {
if ( status = = STATUS_TIMEOUT ) {
// We get a read timeout if no card is selected yet, so let's select one
// Wake the card up again if sleeping
// byte atqa_answer[2];
// byte atqa_size = 2;
// PICC_WakeupA(atqa_answer, &atqa_size);
if ( ! PICC_IsNewCardPresent ( ) | | ! PICC_ReadCardSerial ( ) ) {
Serial . println ( F ( " No card was previously selected, and none are available. Failed to set UID. " ) ) ;
return false ;
}
status = PCD_Authenticate ( MFRC522 : : PICC_CMD_MF_AUTH_KEY_A , ( byte ) 1 , & key , & uid ) ;
if ( status ! = STATUS_OK ) {
// We tried, time to give up
if ( logErrors ) {
Serial . println ( F ( " Failed to authenticate to card for reading, could not set UID: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
}
else {
if ( logErrors ) {
Serial . print ( F ( " PCD_Authenticate() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
}
// Read block 0
byte block0_buffer [ 18 ] ;
byte byteCount = sizeof ( block0_buffer ) ;
status = MIFARE_Read ( ( byte ) 0 , block0_buffer , & byteCount ) ;
if ( status ! = STATUS_OK ) {
if ( logErrors ) {
Serial . print ( F ( " MIFARE_Read() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
Serial . println ( F ( " Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF? " ) ) ;
}
return false ;
}
// Write new UID to the data we just read, and calculate BCC byte
byte bcc = 0 ;
for ( int i = 0 ; i < uidSize ; i + + ) {
block0_buffer [ i ] = newUid [ i ] ;
bcc ^ = newUid [ i ] ;
}
// Write BCC byte to buffer
block0_buffer [ uidSize ] = bcc ;
// Stop encrypted traffic so we can send raw bytes
PCD_StopCrypto1 ( ) ;
// Activate UID backdoor
if ( ! MIFARE_OpenUidBackdoor ( logErrors ) ) {
if ( logErrors ) {
Serial . println ( F ( " Activating the UID backdoor failed. " ) ) ;
}
return false ;
}
// Write modified block 0 back to card
status = MIFARE_Write ( ( byte ) 0 , block0_buffer , ( byte ) 16 ) ;
if ( status ! = STATUS_OK ) {
if ( logErrors ) {
Serial . print ( F ( " MIFARE_Write() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
// Wake the card up again
byte atqa_answer [ 2 ] ;
byte atqa_size = 2 ;
PICC_WakeupA ( atqa_answer , & atqa_size ) ;
return true ;
}
/**
* Resets entire sector 0 to zeroes , so the card can be read again by readers .
*/
bool MFRC522 : : MIFARE_UnbrickUidSector ( bool logErrors ) {
MIFARE_OpenUidBackdoor ( logErrors ) ;
byte block0_buffer [ ] = { 0x01 , 0x02 , 0x03 , 0x04 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
// Write modified block 0 back to card
byte status = MIFARE_Write ( ( byte ) 0 , block0_buffer , ( byte ) 16 ) ;
if ( status ! = STATUS_OK ) {
if ( logErrors ) {
Serial . print ( F ( " MIFARE_Write() failed: " ) ) ;
Serial . println ( GetStatusCodeName ( status ) ) ;
}
return false ;
}
return true ;
}
/////////////////////////////////////////////////////////////////////////////////////
// Convenience functions - does not add extra functionality
/////////////////////////////////////////////////////////////////////////////////////
/**
* Returns true if a PICC responds to PICC_CMD_REQA .
* Only " new " cards in state IDLE are invited . Sleeping cards in state HALT are ignored .
*
* @ return bool
*/
bool MFRC522 : : PICC_IsNewCardPresent ( ) {
byte bufferATQA [ 2 ] ;
byte bufferSize = sizeof ( bufferATQA ) ;
byte result = PICC_RequestA ( bufferATQA , & bufferSize ) ;
return ( result = = STATUS_OK | | result = = STATUS_COLLISION ) ;
} // End PICC_IsNewCardPresent()
/**
* Simple wrapper around PICC_Select .
* Returns true if a UID could be read .
* Remember to call PICC_IsNewCardPresent ( ) , PICC_RequestA ( ) or PICC_WakeupA ( ) first .
* The read UID is available in the class variable uid .
*
* @ return bool
*/
bool MFRC522 : : PICC_ReadCardSerial ( ) {
byte result = PICC_Select ( & uid ) ;
return ( result = = STATUS_OK ) ;
} // End PICC_ReadCardSerial()