diff --git a/MFRC522.cpp b/MFRC522.cpp index 5e09b29..ff3defa 100644 --- a/MFRC522.cpp +++ b/MFRC522.cpp @@ -1,6 +1,6 @@ /* * MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. -* _Please_ see the comments in MFRC522.h - they give useful hints and background. +* NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. * Released into the public domain. */ @@ -10,6 +10,11 @@ ///////////////////////////////////////////////////////////////////////////////////// // Functions for setting up the Arduino ///////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +MFRC522::MFRC522() { +} // End constructor /** * Constructor. @@ -18,18 +23,8 @@ MFRC522::MFRC522( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) ) { - // Set the chipSelectPin as digital output, do not select the slave yet _chipSelectPin = chipSelectPin; - pinMode(_chipSelectPin, OUTPUT); - digitalWrite(_chipSelectPin, HIGH); - - // Set the resetPowerDownPin as digital output, do not reset or power down. _resetPowerDownPin = resetPowerDownPin; - pinMode(_resetPowerDownPin, OUTPUT); - digitalWrite(_resetPowerDownPin, LOW); - - // Set SPI bus to work with MFRC522 chip. - setSPIConfig(); } // End constructor /** @@ -53,7 +48,7 @@ void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One o byte value ///< The value to write. ) { digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg & 0x7E); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + SPI.transfer(reg & 0x7E); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. SPI.transfer(value); digitalWrite(_chipSelectPin, HIGH); // Release slave again } // End PCD_WriteRegister() @@ -100,21 +95,21 @@ void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One o if (count == 0) { return; } - //Serial.print("Reading "); Serial.print(count); Serial.println(" bytes from register."); + //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); byte address = 0x80 | (reg & 0x7E); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. byte index = 0; // Index in values array. digitalWrite(_chipSelectPin, LOW); // Select slave count--; // One read is performed outside of the loop SPI.transfer(address); // Tell MFRC522 which address we want to read while (index < count) { - if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] + 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 = SPI.transfer(address); + byte value = SPI.transfer(address); // Apply mask to both current value of values[0] and the new data in value. values[0] = (values[index] & ~mask) | (value & mask); } @@ -155,21 +150,21 @@ void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. O * * @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. +MFRC522::StatusCode 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_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(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�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 + 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; } @@ -177,7 +172,7 @@ byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to tra return STATUS_TIMEOUT; } } - PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + 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); @@ -194,8 +189,18 @@ byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to tra * Initializes the MFRC522 chip. */ void MFRC522::PCD_Init() { - if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. - digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. + // Set the chipSelectPin as digital output, do not select the slave yet + pinMode(_chipSelectPin, OUTPUT); + digitalWrite(_chipSelectPin, HIGH); + + // Set the resetPowerDownPin as digital output, do not reset or power down. + pinMode(_resetPowerDownPin, OUTPUT); + + // Set SPI bus to work with MFRC522 chip. + setSPIConfig(); + + 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�s. Let us be generous: 50ms. delay(50); } @@ -206,16 +211,28 @@ void MFRC522::PCD_Init() { // 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�s. - PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. - PCD_WriteRegister(TReloadRegL, 0xE8); + 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�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() +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + _chipSelectPin = chipSelectPin; + _resetPowerDownPin = resetPowerDownPin; + // Set the chipSelectPin as digital output, do not select the slave yet + PCD_Init(); +} // End PCD_Init() + /** * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. */ @@ -233,7 +250,7 @@ void MFRC522::PCD_Reset() { /** * Turns the antenna on by enabling pins TX1 and TX2. - * After a reset these pins disabled. + * After a reset these pins are disabled. */ void MFRC522::PCD_AntennaOn() { byte value = PCD_ReadRegister(TxControlReg); @@ -242,6 +259,114 @@ void MFRC522::PCD_AntennaOn() { } } // 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)=01110000b 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)=01110000b 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 ///////////////////////////////////////////////////////////////////////////////////// @@ -252,48 +377,48 @@ void MFRC522::PCD_AntennaOn() { * * @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. +MFRC522::StatusCode 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 commend, waits for completion and transfers data back from the FIFO. + * 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. +MFRC522::StatusCode 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] + 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_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_WriteRegister(CommandReg, command); // Execute the command + if (command == PCD_Transceive) { PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts } @@ -302,7 +427,7 @@ byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execut // Each iteration of the do-while-loop takes 17.86�s. i = 2000; while (1) { - n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + 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; } @@ -315,27 +440,27 @@ byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execut } // 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 + 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 + 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. + *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 + if (errorRegValue & 0x08) { // CollErr return STATUS_COLLISION; } @@ -350,10 +475,10 @@ byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execut 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; + byte controlBuffer[2]; + MFRC522::StatusCode status = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (status != STATUS_OK) { + return status; } if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { return STATUS_CRC_WRONG; @@ -369,9 +494,9 @@ byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execut * * @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. - ) { +MFRC522::StatusCode 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() @@ -381,9 +506,9 @@ byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA * * @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. - ) { +MFRC522::StatusCode 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() @@ -393,18 +518,18 @@ byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA * * @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. - ) { +MFRC522::StatusCode 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; + MFRC522::StatusCode 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] + 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; @@ -432,18 +557,18 @@ byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC * * @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. - ) { +MFRC522::StatusCode 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 cascadeLevel = 1; + MFRC522::StatusCode result; byte count; byte index; byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. - char currentLevelKnownBits; // The number of known UID bits 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. @@ -452,13 +577,13 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp 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 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. @@ -477,13 +602,13 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp if (validBits > 80) { return STATUS_INVALID; } - + // Prepare MFRC522 - PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. - + 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) { + 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: @@ -522,7 +647,7 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp 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) { + if (bytesToCopy > maxBytes) { bytesToCopy = maxBytes; } for (count = 0; count < bytesToCopy; count++) { @@ -536,12 +661,12 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. selectDone = false; - while ( ! selectDone) { + 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("SELECT: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes - // Calulate BCC - Block Check Character + // 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]); @@ -555,7 +680,7 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp responseLength = 3; } else { // This is an ANTICOLLISION. - //Serial.print("ANTICOLLISION: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + //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 @@ -565,16 +690,16 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp 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); + 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 + byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (valueOfCollReg & 0x20) { // CollPosNotValid return STATUS_COLLISION; // Without a valid collision position we cannot continue } byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. @@ -588,7 +713,7 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp 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); + buffer[index] |= (1 << count); } else if (result != STATUS_OK) { return result; @@ -604,8 +729,8 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp // Run loop again to do the SELECT. } } - } // End of while ( ! selectDone) - + } // 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[] @@ -616,7 +741,7 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp } // Check response SAK (Select Acknowledge) - if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). + 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. @@ -634,11 +759,11 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp uidComplete = true; uid->sak = responseBuffer[0]; } - } // End of while ( ! uidComplete) + } // End of while (!uidComplete) // Set correct uid->size uid->size = 3 * cascadeLevel + 1; - + return STATUS_OK; } // End PICC_Select() @@ -647,10 +772,10 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp * * @return STATUS_OK on success, STATUS_??? otherwise. */ -byte MFRC522::PICC_HaltA() { - byte result; - byte buffer[4]; - +MFRC522::StatusCode MFRC522::PICC_HaltA() { + MFRC522::StatusCode result; + byte buffer[4]; + // Build command buffer buffer[0] = PICC_CMD_HLTA; buffer[1] = 0; @@ -659,7 +784,7 @@ byte MFRC522::PICC_HaltA() { 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 @@ -692,11 +817,11 @@ byte MFRC522::PICC_HaltA() { * * @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. - ) { +MFRC522::StatusCode 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 @@ -720,7 +845,7 @@ byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PIC */ void MFRC522::PCD_StopCrypto1() { // Clear MFCrypto1On bit - PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] + PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] } // End PCD_StopCrypto1() /** @@ -739,17 +864,17 @@ void MFRC522::PCD_StopCrypto1() { * * @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; +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode result; // Sanity check if (buffer == NULL || *bufferSize < 18) { return STATUS_NO_ROOM; } - + // Build command buffer buffer[0] = PICC_CMD_MF_READ; buffer[1] = blockAddr; @@ -768,23 +893,23 @@ byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0x * * For MIFARE Classic the sector containing the block must be authenticated before calling this function. * - * For MIFARE Ultralight the opretaion is called "COMPATIBILITY WRITE". + * 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 04h to 0Fh 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; - +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode 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]; @@ -794,13 +919,13 @@ byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0x 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. + 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() @@ -809,17 +934,17 @@ byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0x * * @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; - +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode result; + // Sanity check if (buffer == NULL || bufferSize < 4) { return STATUS_INVALID; } - + // Build commmand buffer byte cmdBuffer[6]; cmdBuffer[0] = PICC_CMD_UL_WRITE; @@ -842,9 +967,9 @@ byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to writ * * @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. - ) { +MFRC522::StatusCode 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() @@ -856,9 +981,9 @@ byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. * * @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. - ) { +MFRC522::StatusCode 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() @@ -870,8 +995,8 @@ byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. * * @return STATUS_OK on success, STATUS_??? otherwise. */ -byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. - ) { +MFRC522::StatusCode 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); @@ -882,13 +1007,13 @@ byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. * * @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; +MFRC522::StatusCode 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 + ) { + MFRC522::StatusCode 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; @@ -896,13 +1021,13 @@ byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use 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() @@ -913,11 +1038,11 @@ byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use * * @return STATUS_OK on success, STATUS_??? otherwise. */ -byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. - ) { - byte result; +MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. + ) { + MFRC522::StatusCode 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; @@ -928,6 +1053,62 @@ byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { + MFRC522::StatusCode 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. + */ +MFRC522::StatusCode 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 @@ -939,13 +1120,13 @@ byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. * * @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; +MFRC522::StatusCode 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 + ) { + MFRC522::StatusCode 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; @@ -981,24 +1162,23 @@ byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data t } // End PCD_MIFARE_Transceive() /** - * Returns a string pointer to a status code name. + * Returns a __FlashStringHelper pointer to a status code name. * + * @return const __FlashStringHelper * */ -const char *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. +const __FlashStringHelper *MFRC522::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums. ) { switch (code) { - case STATUS_OK: return "Success."; break; - case STATUS_ERROR: return "Error in communication."; break; - case STATUS_COLLISION: return "Collission detected."; break; - case STATUS_TIMEOUT: return "Timeout in communication."; break; - case STATUS_NO_ROOM: return "A buffer is not big enough."; break; - case STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; break; - case STATUS_INVALID: return "Invalid argument."; break; - case STATUS_CRC_WRONG: return "The CRC_A does not match."; break; - case STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; break; - default: - return "Unknown error"; - break; + case STATUS_OK: return F("Success."); + case STATUS_ERROR: return F("Error in communication."); + case STATUS_COLLISION: return F("Collission detected."); + case STATUS_TIMEOUT: return F("Timeout in communication."); + case STATUS_NO_ROOM: return F("A buffer is not big enough."); + case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); + case STATUS_INVALID: return F("Invalid argument."); + case STATUS_CRC_WRONG: return F("The CRC_A does not match."); + case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); + default: return F("Unknown error"); } } // End GetStatusCodeName() @@ -1007,52 +1187,47 @@ const char *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enum * * @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; - } - +MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). + ) { + // http://www.nxp.com/documents/application_note/AN10833.pdf + // 3.2 Coding of Select Acknowledge (SAK) + // ignore 8-bit (iso14443 starts with LSBit = bit 1) + // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) + sak &= 0x7F; 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 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete + case 0x09: return PICC_TYPE_MIFARE_MINI; + case 0x08: return PICC_TYPE_MIFARE_1K; + case 0x18: return PICC_TYPE_MIFARE_4K; + case 0x00: return PICC_TYPE_MIFARE_UL; case 0x10: - case 0x11: return PICC_TYPE_MIFARE_PLUS; break; - case 0x01: return PICC_TYPE_TNP3XXX; break; - default: break; + case 0x11: return PICC_TYPE_MIFARE_PLUS; + case 0x01: return PICC_TYPE_TNP3XXX; + case 0x20: return PICC_TYPE_ISO_14443_4; + case 0x40: return PICC_TYPE_ISO_18092; + default: return PICC_TYPE_UNKNOWN; } - - 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 string pointer to the PICC type name. + * Returns a __FlashStringHelper pointer to the PICC type name. * + * @return const __FlashStringHelper * */ -const char *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. - ) { +const __FlashStringHelper *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. + ) { switch (piccType) { - case PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; break; - case PICC_TYPE_ISO_18092: return "PICC compliant with ISO/IEC 18092 (NFC)"; break; - case PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; break; - case PICC_TYPE_MIFARE_1K: return "MIFARE 1KB"; break; - case PICC_TYPE_MIFARE_4K: return "MIFARE 4KB"; break; - case PICC_TYPE_MIFARE_UL: return "MIFARE Ultralight or Ultralight C"; break; - case PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; break; - case PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; break; - case PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; break; + case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); + case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)"); + case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); + case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); + case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); + case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); + case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); + case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); + case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); case PICC_TYPE_UNKNOWN: - default: return "Unknown type"; break; + default: return F("Unknown type"); } } // End PICC_GetTypeName() @@ -1066,16 +1241,19 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro MIFARE_Key key; // UID - Serial.print("Card UID:"); + Serial.print(F("Card UID:")); for (byte i = 0; i < uid->size; i++) { - Serial.print(uid->uidByte[i] < 0x10 ? " 0" : " "); + 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("PICC type: "); + Serial.print(F("PICC type: ")); Serial.println(PICC_GetTypeName(piccType)); // Dump contents @@ -1094,11 +1272,11 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro PICC_DumpMifareUltralightToSerial(); break; - case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_ISO_14443_4: case PICC_TYPE_ISO_18092: case PICC_TYPE_MIFARE_PLUS: case PICC_TYPE_TNP3XXX: - Serial.println("Dumping memory contents not implemented for that PICC type."); + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); break; case PICC_TYPE_UNKNOWN: @@ -1106,7 +1284,7 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro default: break; // No memory dump here } - + Serial.println(); PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. } // End PICC_DumpToSerial() @@ -1137,13 +1315,13 @@ void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid str break; default: // Should not happen. Ignore. - break; + break; } // Dump sectors, highest address first. if (no_of_sectors) { - Serial.println("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits"); - for (char i = no_of_sectors - 1; i >= 0; i--) { + 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); } } @@ -1160,11 +1338,11 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U MIFARE_Key *key, ///< Key A for the sector. byte sector ///< The sector to dump, 0..39. ) { - byte status; + MFRC522::StatusCode 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) @@ -1179,7 +1357,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U 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; @@ -1198,26 +1376,36 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U byte buffer[18]; byte blockAddr; isSectorTrailer = true; - for (char blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { + for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { blockAddr = firstBlock + blockOffset; // Sector number - only on first line if (isSectorTrailer) { - Serial.print(sector < 10 ? " " : " "); // Pad with spaces + if(sector < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces Serial.print(sector); - Serial.print(" "); + Serial.print(F(" ")); } else { - Serial.print(" "); + Serial.print(F(" ")); } // Block number - Serial.print(blockAddr < 10 ? " " : (blockAddr < 100 ? " " : " ")); // Pad with spaces + 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(" "); + 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("PCD_Authenticate() failed: "); + Serial.print(F("PCD_Authenticate() failed: ")); Serial.println(GetStatusCodeName(status)); return; } @@ -1226,16 +1414,19 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U byteCount = sizeof(buffer); status = MIFARE_Read(blockAddr, buffer, &byteCount); if (status != STATUS_OK) { - Serial.print("MIFARE_Read() failed: "); + Serial.print(F("MIFARE_Read() failed: ")); Serial.println(GetStatusCodeName(status)); continue; } // Dump data for (byte index = 0; index < 16; index++) { - Serial.print(buffer[index] < 0x10 ? " 0" : " "); + if(buffer[index] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); Serial.print(buffer[index], HEX); if ((index % 4) == 3) { - Serial.print(" "); + Serial.print(F(" ")); } } // Parse sector trailer data @@ -1253,7 +1444,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U 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; @@ -1266,20 +1457,20 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (firstInGroup) { // Print access bits - Serial.print(" [ "); - Serial.print((g[group] >> 2) & 1, DEC); Serial.print(" "); - Serial.print((g[group] >> 1) & 1, DEC); Serial.print(" "); + 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(" ] "); + Serial.print(F(" ] ")); if (invertedError) { - Serial.print(" Inverted access bits did not match! "); + 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(" Value=0x"); Serial.print(value, HEX); - Serial.print(" Adr=0x"); Serial.print(buffer[12], HEX); + Serial.print(F(" Value=0x")); Serial.print(value, HEX); + Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); } Serial.println(); } @@ -1291,31 +1482,37 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U * Dumps memory contents of a MIFARE Ultralight PICC. */ void MFRC522::PICC_DumpMifareUltralightToSerial() { - byte status; + MFRC522::StatusCode status; byte byteCount; byte buffer[18]; byte i; - - Serial.println("Page 0 1 2 3"); + + 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("MIFARE_Read() failed: "); + Serial.print(F("MIFARE_Read() failed: ")); Serial.println(GetStatusCodeName(status)); break; } // Dump data for (byte offset = 0; offset < 4; offset++) { i = page + offset; - Serial.print(i < 10 ? " " : " "); // Pad with spaces + if(i < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces Serial.print(i); - Serial.print(" "); + Serial.print(F(" ")); for (byte index = 0; index < 4; index++) { i = 4 * offset + index; - Serial.print(buffer[i] < 0x10 ? " 0" : " "); + if(buffer[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); Serial.print(buffer[i], HEX); } Serial.println(); @@ -1341,6 +1538,207 @@ void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 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; + MFRC522::StatusCode 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}; + MFRC522::StatusCode 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 + MFRC522::StatusCode 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 ///////////////////////////////////////////////////////////////////////////////////// diff --git a/MFRC522.h b/MFRC522.h index dde1234..bbe1963 100644 --- a/MFRC522.h +++ b/MFRC522.h @@ -3,6 +3,7 @@ * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) * Created by Miguel Balboa (circuitito.com), Jan, 2012. * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. * Released into the public domain. * * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. @@ -39,7 +40,7 @@ * Bytes 6-8: Access Bits * Bytes 9: User data * Bytes 10-15: Key B (or user data) - * Block 0 is read only manufacturer data. + * Block 0 is read-only manufacturer data. * To access a block, an authentication using a key from the block's sector must be performed first. * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). * All keys are set to FFFFFFFFFFFFh at chip delivery. @@ -57,13 +58,13 @@ * MIFARE Ultralight (MF0ICU1): * Has 16 pages of 4 bytes = 64 bytes. * Pages 0 + 1 is used for the 7-byte UID. - * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. * MIFARE Ultralight C (MF0ICU2): - * Has 48 pages of 4 bytes = 64 bytes. + * Has 48 pages of 4 bytes = 192 bytes. * Pages 0 + 1 is used for the 7-byte UID. - * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. * Page 40 Lock bytes @@ -77,6 +78,59 @@ #include #include +// Firmware data for self-test +// Reference values based on firmware version +// Hint: if needed, you can remove unused self-test data to save flash memory +// +// Version 0.0 (0x90) +// Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 Sefttest +const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { + 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, + 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, + 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, + 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, + 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, + 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, + 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, + 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D +}; +// Version 1.0 (0x91) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { + 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, + 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, + 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, + 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, + 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, + 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, + 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, + 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 +}; +// Version 2.0 (0x92) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 Self test +const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { + 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, + 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, + 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, + 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, + 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, + 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, + 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, + 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F +}; +// Clone +// Fudan Semiconductor FM17522 (0x88) +const byte FM17522_firmware_reference[] PROGMEM = { + 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, + 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, + 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, + 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, + 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, + 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, + 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, + 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 +}; + class MFRC522 { public: // MFRC522 registers. Described in chapter 9 of the datasheet. @@ -100,7 +154,7 @@ public: CollReg = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface // 0x0F // reserved for future use - // Page 1:Command + // Page 1: Command // 0x10 // reserved for future use ModeReg = 0x11 << 1, // defines general modes for transmitting and receiving TxModeReg = 0x12 << 1, // defines transmission data rate and framing @@ -119,7 +173,7 @@ public: SerialSpeedReg = 0x1F << 1, // selects the speed of the serial UART interface // Page 2: Configuration - // 0x20 // reserved for future use + // 0x20 // reserved for future use CRCResultRegH = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation CRCResultRegL = 0x22 << 1, // 0x23 // reserved for future use @@ -136,7 +190,7 @@ public: TCounterValueRegH = 0x2E << 1, // shows the 16-bit timer value TCounterValueRegL = 0x2F << 1, - // Page 3:Test Registers + // Page 3: Test Registers // 0x30 // reserved for future use TestSel1Reg = 0x31 << 1, // general test signal configuration TestSel2Reg = 0x32 << 1, // general test signal configuration @@ -155,7 +209,7 @@ public: // 0x3F // reserved for production tests }; - // MFRC522 comands. Described in chapter 10 of the datasheet. + // MFRC522 commands. Described in chapter 10 of the datasheet. enum PCD_Command { PCD_Idle = 0x00, // no action, cancels current command execution PCD_Mem = 0x01, // stores 25 bytes into the internal buffer @@ -169,6 +223,22 @@ public: PCD_SoftReset = 0x0F // resets the MFRC522 }; + // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). + // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf + enum PCD_RxGain { + RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum + RxGain_23dB = 0x01 << 4, // 001b - 23 dB + RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b + RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b + RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default + RxGain_38dB = 0x05 << 4, // 101b - 38 dB + RxGain_43dB = 0x06 << 4, // 110b - 43 dB + RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum + RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB + RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB + RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB + }; + // Commands sent to the PICC. enum PICC_Command { // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) @@ -176,8 +246,8 @@ public: PICC_CMD_WUPA = 0x52, // 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. PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 - PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 1 - PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. @@ -200,7 +270,7 @@ public: MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. }; - + // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. enum PICC_Type { PICC_TYPE_UNKNOWN = 0, @@ -249,6 +319,7 @@ public: ///////////////////////////////////////////////////////////////////////////////////// // Functions for setting up the Arduino ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(); MFRC522(byte chipSelectPin, byte resetPowerDownPin); void setSPIConfig(); @@ -262,63 +333,76 @@ public: void setBitMask(unsigned char reg, unsigned char mask); void PCD_SetRegisterBitMask(byte reg, byte mask); void PCD_ClearRegisterBitMask(byte reg, byte mask); - byte PCD_CalculateCRC(byte *data, byte length, byte *result); - + MFRC522::StatusCode PCD_CalculateCRC(byte *data, byte length, byte *result); + ///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 ///////////////////////////////////////////////////////////////////////////////////// void PCD_Init(); + void PCD_Init(byte chipSelectPin, byte resetPowerDownPin); void PCD_Reset(); void PCD_AntennaOn(); + void PCD_AntennaOff(); + byte PCD_GetAntennaGain(); + void PCD_SetAntennaGain(byte mask); + bool PCD_PerformSelfTest(); ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with PICCs ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); - byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); - - byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); - byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); - byte PICC_REQA_or_WUPA( byte command, byte *bufferATQA, byte *bufferSize); - byte PICC_Select(Uid *uid, byte validBits = 0); - byte PICC_HaltA(); + MFRC522::StatusCode PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + MFRC522::StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + MFRC522::StatusCode PICC_RequestA(byte *bufferATQA, byte *bufferSize); + MFRC522::StatusCode PICC_WakeupA(byte *bufferATQA, byte *bufferSize); + MFRC522::StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); + MFRC522::StatusCode PICC_Select(Uid *uid, byte validBits = 0); + MFRC522::StatusCode PICC_HaltA(); ///////////////////////////////////////////////////////////////////////////////////// // Functions for communicating with MIFARE PICCs ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + MFRC522::StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); void PCD_StopCrypto1(); - byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); - byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); - byte MIFARE_Decrement(byte blockAddr, long delta); - byte MIFARE_Increment(byte blockAddr, long delta); - byte MIFARE_Restore(byte blockAddr); - byte MIFARE_Transfer(byte blockAddr); - byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + MFRC522::StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); + MFRC522::StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); + MFRC522::StatusCode MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + MFRC522::StatusCode MIFARE_Decrement(byte blockAddr, long delta); + MFRC522::StatusCode MIFARE_Increment(byte blockAddr, long delta); + MFRC522::StatusCode MIFARE_Restore(byte blockAddr); + MFRC522::StatusCode MIFARE_Transfer(byte blockAddr); + MFRC522::StatusCode MIFARE_GetValue(byte blockAddr, long *value); + MFRC522::StatusCode MIFARE_SetValue(byte blockAddr, long value); ///////////////////////////////////////////////////////////////////////////////////// // Support functions ///////////////////////////////////////////////////////////////////////////////////// - byte PCD_MIFARE_Transceive( byte *sendData, byte sendLen, bool acceptTimeout = false); - const char *GetStatusCodeName(byte code); - byte PICC_GetType(byte sak); - const char *PICC_GetTypeName(byte type); + MFRC522::StatusCode PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *GetStatusCodeName(byte code); + const __FlashStringHelper *GetStatusCodeName(MFRC522::StatusCode code); + MFRC522::PICC_Type PICC_GetType(byte sak); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *PICC_GetTypeName(byte type); + const __FlashStringHelper *PICC_GetTypeName(byte type); void PICC_DumpToSerial(Uid *uid); void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); void PICC_DumpMifareUltralightToSerial(); void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + bool MIFARE_OpenUidBackdoor(bool logErrors); + bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); + bool MIFARE_UnbrickUidSector(bool logErrors); ///////////////////////////////////////////////////////////////////////////////////// // Convenience functions - does not add extra functionality ///////////////////////////////////////////////////////////////////////////////////// bool PICC_IsNewCardPresent(); - bool PICC_ReadCardSerial(); - + bool PICC_ReadCardSerial(); + private: byte _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) - byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); + MFRC522::StatusCode MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); }; #endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c737e0 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +# Makefile for MFRC522 library +# + +all: package + +help: + @echo "Please use \`make ' where is one of" + @echo " clean to clean the project (e.g. remove process files)" + @echo " package to package the library (into a zip file)" + +clean: + rm ./MFRC522.zip + @echo + @echo "Clean finished." + +package: + zip -o ./MFRC522.zip ./MFRC522.h ./MFRC522.cpp diff --git a/README.md b/README.md deleted file mode 100644 index 2cf4daa..0000000 --- a/README.md +++ /dev/null @@ -1,41 +0,0 @@ -rduino RFID Library for MFRC522 -======================================================= -This library is use to read RFID Cards through MIFARE RC522 reader using SPI interface on Arduino Board. - - - -Pin Layout for Arduino UNO ------------- -* MOSI: Pin 11 / ICSP-4 -* MISO: Pin 12 / ICSP-1 -* SCK : Pin 13 / ISCP-3 -* SS : Pin 10 (Configurable) -* RST : Pin 9 (Configurable) - -Pin Layout for Arduino Mega ------------- -* MOSI: Pin 51 / ICSP-4 -* MISO: Pin 50 / ICSP-1 -* SCK : Pin 52 / ISCP-3 -* SS : Pin 53 (Configurable) -* RST : Pin 5 (Configurable) - -Installation ------------- -Clone the repository with: - -```bash -git clone https://github.com/sophiekovalevsky/rfid.git -``` -And place the repo into the your arduino libraries folder. - -Credits ------ -This library was created by [Miguel Balboa.][1] - -More comming soon. ------ -We need to document a lot. - -[1]: https://github.com/miguelbalboa - diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..d284f6f --- /dev/null +++ b/README.rst @@ -0,0 +1,179 @@ +MFRC522 +======= + +Arduino library for MFRC522 and other RFID RC522 based modules. + +Read and write different types of Radio-Frequency IDentification (RFID) cards +on your Arduino using a RC522 based reader connected via the Serial Peripheral +Interface (SPI) interface. + +Set the UID, write to sector 0, and unbrick Chinese UID changeable MIFARE cards. + + +.. _pin layout: + +Pin Layout +---------- + +The following table shows the typical pin layout used: + ++-----------+----------+-------------------------------------------------------------+--------+ +| | PCD | Arduino | Teensy | +| +----------+-------------+---------+---------+---------------+-----------+--------+ +| | MFRC522 | Uno | Mega | Nano v3 |Leonardo/Micro | Pro Micro | 3.1 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ +| Signal | Pin | Pin | Pin | Pin | Pin | Pin | Pin | ++===========+==========+=============+=========+=========+===============+===========+========+ +| RST/Reset | RST | 9 [1]_ | 5 [1]_ | D9 | RESET/ICSP-5 | RST | 9 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ +| SPI SS | SDA [3]_ | 10 [2]_ | 53 [2]_ | D10 | 10 | 10 | 10 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ +| SPI MOSI | MOSI | 11 / ICSP-4 | 51 | D11 | ICSP-4 | 16 | 11 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ +| SPI MISO | MISO | 12 / ICSP-1 | 50 | D12 | ICSP-1 | 14 | 12 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ +| SPI SCK | SCK | 13 / ICSP-3 | 52 | D13 | ICSP-3 | 15 | 13 | ++-----------+----------+-------------+---------+---------+---------------+-----------+--------+ + +.. [1] Configurable, typically defined as RST_PIN in sketch/program. +.. [2] Configurable, typically defined as SS_PIN in sketch/program. +.. [3] The SDA pin might be labeled SS on some/older MFRC522 boards. + + +Hardware +-------- + +There are three hardware components involved: + +1. **Micro Controller**: + +* An `Arduino`_ or compatible executing the Sketch using this library. + +* Prices vary from USD 7 for clones, to USD 75 for "starter kits" (which + might be a good choice if this is your first exposure to Arduino; + check if such kit already includes the Arduino, Reader, and some Tags). + +2. **Proximity Coupling Device (PCD)**: + +* The PCD is the actual RFID **Reader** based on `NXP MFRC522`_ Contactless + Reader Integrated Circuit). + +* Readers can be found on `eBay`_ for around USD 5: search for *"rc522"*. + +* You can also find them at several web stores, they are often included in + *"starter kits"*; so check your favourite electronics provider as well. + +3. **Proximity Integrated Circuit Card (PICC)**: + +* The PICC is the RFID **Card** or **Tag** using the `ISO/IEC 14443A`_ + interface, for example Mifare or NTAG203. + +* One or two might be included with the Reader or *"starter kit"* already. + + +Protocols +--------- + +1. The micro controller and the reader use SPI for communication. + +* The protocol is described in the `NXP MFRC522`_ datasheet. + +* See the `Pin Layout`_ section for details on connecting the pins. + +2. The reader and the tags communicate using a 13.56 MHz electromagnetic field. + +* The protocol is defined in ISO/IEC 14443-3:2011 Part 3 Type A. + + * Details are found in chapter 6 *"Type A – Initialization and anticollision"*. + + * See http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf for a free version + of the final draft (which might be outdated in some areas). + + * The reader do not support ISO/IEC 14443-3 Type B. + + +Troubleshooting +------- + +* **I don't get input from reader** or **WARNING: Communication failure, is the MFRC522 properly connected?** + + #. Check your connection, see `Pin Layout`_ . + #. Check voltage. Most breakouts work with 3.3V. + #. The SPI only works with 3.3V, most breakouts seems 5V tollerant, but try a level shifter. + + +* **Sometimes I get timeouts** or **tag/card sometimes not work.** + + #. Try other site of the antenna. + #. Try to decrease distance between MFRC522. + #. Increase antenna gain per firmware: ``mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);`` + #. Use better power supply. + #. Hardware corrupted, most products are from china and sometimes the quality is really low. Contact your seller. + + +* **My tag/card doesn't work.** + + #. Distance between antenna and token too huge (>1cm). + #. You got wrong PICC. Is it really 13.56 MHz? Is it really a Mifare Type A? + #. NFC tokens are not supported. Some may work. + #. Animal marker are not supported. They use other frequency. + #. Hardware corrupted, most products are from china and sometimes the quality is really low. Contact your seller. + +* **My mobile phone doesn't recognize the MFRC522** or **my MFRC522 can't read data from other MFRC522** + + #. Card simmulation is not supported. + #. Communication with mobile phones is not supported. + #. Peer to peer communication is not supported. + +* **I need more features.** + + #. If software: code it and make a pull request. + #. If hardware: buy a more expensive like PN532 (supports NFC and many more, but costs about $15) + + +License +------- +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to http://unlicense.org/ + + +History +------- + +The MFRC522 library was first created in Jan 2012 by Miguel Balboa (from +http://circuitito.com) based on code by Dr. Leong (from http://B2CQSHOP.com) +for *"Arduino RFID module Kit 13.56 Mhz with Tags SPI W and R By COOQRobot"*. + +It was translated into English and rewritten/refactored in the fall of 2013 +by Søren Thing Andersen (from http://access.thing.dk). + +It has been extended with functionality to alter sector 0 on Chinese UID changeable MIFARE card in Oct 2014 by Tom Clement (from http://tomclement.nl). + + +.. _arduino: http://arduino.cc/ +.. _ebay: http://www.ebay.com/ +.. _iso/iec 14443a: http://en.wikipedia.org/wiki/ISO/IEC_14443 +.. _iso/iec 14443-3\:2011 part 3: +.. _nxp mfrc522: http://www.nxp.com/documents/data_sheet/MFRC522.pdf diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 5e936bf..0000000 --- a/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -TODO: - -Create Examples: -- Read more stuff than the serial number -- Write to a card -- Do some ethernet stuff (with an ethernet module) diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..00d2e13 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to \ No newline at end of file diff --git a/changes.txt b/changes.txt index cefa4a7..0618674 100644 --- a/changes.txt +++ b/changes.txt @@ -1,9 +1,57 @@ -Renamed library from RFID to MFRC522 (RFID seemed to generic). -Register names changed to comply with datasheet. -Global defines moved into class. -All constants, functions and parameters are now commented in English. -Code refactored, most function names have changed. -Now supports ISO-14443-3 anti collission and 4/7/10 byte UIDs (cascade levels). -Added functions for MIFARE Classic Decrement/Increment/Restore/Transfer and MIFARE Ultralight Write. -New examples written. +5 Dec 2015 +- recognize infineon cards correctly @mayatforest +- added multi reader support, see example @lmmeng +- functions return MFRC522::StatusCode instead of generic byte @rotzbua +10 Nov 2014 +- Updated the changelog. +- Added makefile. + +24 Oct 2014 +- Added PlatformIO-based manifest file. + +17 Jul 2014 +- Written documentation for the library. +- Added rfid_default_keys example. + +11 Jun 2014 +- Updated example: ReadAndWrite. + +14 Apr 2014 +- Updated examples: DumpInfo, MifareClassicValueBlock, and ReadAndWrite. + +12 Feb 2014 +- Fixed resetPowerDownPin initial state. + +29 Jan 2014 +- Fixed chipSelectPin initial state. + +30 Nov 2013 +- Examples put in their own folders. +- Updated the keywords.txt file. + +12 Nov 2013 +- Updated examples: DumpInfo, MifareClassicValueBlock, and ReadAndWrite. + +20 Oct 2013 +- All constants, functions and parameters are now commented in English. +- Code refactored, most function names have changed. +- Support ISO-14443-3 anti collission and 4/7/10 byte UIDs (cascade levels). +- Added functions for MIFARE Classic Decrement/Increment/Restore/Transfer + and MIFARE Ultralight Write. +- New examples written. + +19 Oct 2013 +- Renamed library from RFID to MFRC522 (RFID seemed to generic). +- Register names changed to comply with datasheet. +- Global defines moved into class. + +24 Sep 2013 +- Turn off encryption when tag is halted. + +27 Jan 2013 +- Added README and small TODO list. +- Added example to show Serial on LCD display. + +09 Sep 2012 +- Initial commit to GitHub. diff --git a/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz b/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz new file mode 100644 index 0000000..7c98c49 Binary files /dev/null and b/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz differ diff --git a/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png b/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png new file mode 100644 index 0000000..3e5f59d Binary files /dev/null and b/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png differ diff --git a/doc/fritzing/RFID-RC522 - Pin Layout.png b/doc/fritzing/RFID-RC522 - Pin Layout.png new file mode 100755 index 0000000..5b4f842 Binary files /dev/null and b/doc/fritzing/RFID-RC522 - Pin Layout.png differ diff --git a/doc/fritzing/RFID-RC522-v2.fzpz b/doc/fritzing/RFID-RC522-v2.fzpz new file mode 100644 index 0000000..1866b04 Binary files /dev/null and b/doc/fritzing/RFID-RC522-v2.fzpz differ diff --git a/doc/fritzing/RFID-RC522-v2.png b/doc/fritzing/RFID-RC522-v2.png new file mode 100644 index 0000000..4444909 Binary files /dev/null and b/doc/fritzing/RFID-RC522-v2.png differ diff --git a/doc/rfidmifare.doc b/doc/rfidmifare.doc new file mode 100644 index 0000000..48fee92 Binary files /dev/null and b/doc/rfidmifare.doc differ diff --git a/doc/rfidmifare.pdf b/doc/rfidmifare.pdf new file mode 100644 index 0000000..526c71d Binary files /dev/null and b/doc/rfidmifare.pdf differ diff --git a/examples/ChangeUID/ChangeUID.ino b/examples/ChangeUID/ChangeUID.ino new file mode 100644 index 0000000..556ec7e --- /dev/null +++ b/examples/ChangeUID/ChangeUID.ino @@ -0,0 +1,124 @@ +/* + * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * The library file MFRC522.h has a wealth of useful info. Please read it. + * The functions are documented in MFRC522.cpp. + * + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. + * + * Released into the public domain. + * + * This sample shows how to set the UID on a UID changeable MIFARE card. + * + ----------------------------------------------------------------------------- empty_skull + + - Aggiunti pin per arduino Mega + - Scritto semplice codice per la scrittura e lettura + + - add pin configuration for arduino mega + - write simple read/write Code for new entry user + + http://mac86project.altervista.org/ + + ----------------------------------------------------------------------------- Nicola Coppola + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. + */ + +#include +#include + +/* Set your new UID here! */ +#define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF} +#define SS_PIN 10 +#define RST_PIN 9 + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. +MFRC522::MIFARE_Key key; + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Warning: this example overwrites the UID of your UID changeable card, use with care!")); + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } +} + +// Setting the UID can be as simple as this: +//void loop() { +// byte newUid[] = NEW_UID; +// if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) { +// Serial.println("Wrote new UID to card."); +// } +// delay(1000); +//} + +// But of course this is a more proper approach +void loop() { + + // Look for new cards, and select one if present + if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { + delay(50); + return; + } + + // Now a card is selected. The UID and SAK is in mfrc522.uid. + + // Dump UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < mfrc522.uid.size; i++) { + Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); + Serial.print(mfrc522.uid.uidByte[i], HEX); + } + Serial.println(); + + // Dump PICC type +// byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); +// Serial.print(F("PICC type: ")); +// Serial.print(mfrc522.PICC_GetTypeName(piccType)); +// Serial.print(F(" (SAK ")); +// Serial.print(mfrc522.uid.sak); +// Serial.print(")\r\n"); +// if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI +// && piccType != MFRC522::PICC_TYPE_MIFARE_1K +// && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { +// Serial.println(F("This sample only works with MIFARE Classic cards.")); +// return; +// } + + // Set new UID + byte newUid[] = NEW_UID; + if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) { + Serial.println(F("Wrote new UID to card.")); + } + + // Halt PICC and re-select it so DumpToSerial doesn't get confused + mfrc522.PICC_HaltA(); + if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { + return; + } + + // Dump the new memory contents + Serial.println(F("New UID and contents:")); + mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); + + delay(2000); +} diff --git a/examples/DumpInfo/DumpInfo.ino b/examples/DumpInfo/DumpInfo.ino index ed4fc1d..cfbf6fe 100644 --- a/examples/DumpInfo/DumpInfo.ino +++ b/examples/DumpInfo/DumpInfo.ino @@ -1,44 +1,58 @@ /* - * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. - * The library file MFRC522.h has a wealth of useful info. Please read it. - * The functions are documented in MFRC522.cpp. - * - * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) - * Created by Miguel Balboa (circuitito.com), Jan, 2012. - * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * * Released into the public domain. - * - * Sample program showing how to read data from a PICC using a MFRC522 reader on the Arduino SPI interface. - *----------------------------------------------------------------------------- empty_skull - * Aggiunti pin per arduino Mega - * add pin configuration for arduino mega - * http://mac86project.altervista.org/ - ----------------------------------------------------------------------------- Nicola Coppola - * Pin layout should be as follows: - * Signal Pin Pin Pin - * Arduino Uno Arduino Mega MFRC522 board - * ------------------------------------------------------------ - * Reset 9 5 RST - * SPI SS 10 53 SDA - * SPI MOSI 11 51 MOSI - * SPI MISO 12 50 MISO - * SPI SCK 13 52 SCK - * - * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. + * ---------------------------------------------------------------------------- + * Example sketch/program showing how to read data from a PICC (that is: a RFID + * Tag or Card) using a MFRC522 based RFID Reader on the Arduino SPI interface. + * + * When the Arduino and the MFRC522 module are connected (see the pin layout + * below), load this sketch into Arduino IDE then verify/compile and upload it. + * To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). + * When you present a PICC (that is: a RFID Tag or Card) at reading distance + * of the MFRC522 Reader/PCD, the serial output will show the ID/UID, type and + * any data blocks it can read. Note: you may see "Timeout in communication" + * messages when removing the PICC from reading distance too early. + * + * If your reader supports it, this sketch/program will read all the PICCs + * presented (that is: multiple tag reading). So if you stack two or more + * PICCs on top of each other and present them to the reader, it will first + * output all details of the first and then the next PICC. Note that this + * may take some time as all data blocks are dumped, so keep the PICCs at + * reading distance until complete. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 */ #include #include -#define SS_PIN 10 -#define RST_PIN 9 -MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. +#define RST_PIN 9 // +#define SS_PIN 10 // + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance void setup() { - Serial.begin(9600); // Initialize serial communications with the PC + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) SPI.begin(); // Init SPI bus - mfrc522.PCD_Init(); // Init MFRC522 card - Serial.println("Scan PICC to see UID and type..."); + mfrc522.PCD_Init(); // Init MFRC522 + ShowReaderDetails(); // Show details of PCD - MFRC522 Card Reader details + Serial.println(F("Scan PICC to see UID, type, and data blocks...")); } void loop() { @@ -52,6 +66,24 @@ void loop() { return; } - // Dump debug info about the card. PICC_HaltA() is automatically called. + // Dump debug info about the card; PICC_HaltA() is automatically called mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); } + +void ShowReaderDetails() { + // Get the MFRC522 software version + byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); + Serial.print(F("MFRC522 Software Version: 0x")); + Serial.print(v, HEX); + if (v == 0x91) + Serial.print(F(" = v1.0")); + else if (v == 0x92) + Serial.print(F(" = v2.0")); + else + Serial.print(F(" (unknown)")); + Serial.println(""); + // When 0x00 or 0xFF is returned, communication probably failed + if ((v == 0x00) || (v == 0xFF)) { + Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?")); + } +} diff --git a/examples/FixBrickedUID/FixBrickedUID.ino b/examples/FixBrickedUID/FixBrickedUID.ino new file mode 100644 index 0000000..eea1cf4 --- /dev/null +++ b/examples/FixBrickedUID/FixBrickedUID.ino @@ -0,0 +1,68 @@ +/* + * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * The library file MFRC522.h has a wealth of useful info. Please read it. + * The functions are documented in MFRC522.cpp. + * + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. + * + * Released into the public domain. + * + * This sample shows how to fix a UID changeable MIFARE cards that have a corrupted sector 0. + * + ----------------------------------------------------------------------------- empty_skull + + - Aggiunti pin per arduino Mega + - Scritto semplice codice per la scrittura e lettura + + - add pin configuration for arduino mega + - write simple read/write Code for new entry user + + http://mac86project.altervista.org/ + + ----------------------------------------------------------------------------- Nicola Coppola + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. + */ + +#include +#include + +#define SS_PIN 10 +#define RST_PIN 9 + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. +MFRC522::MIFARE_Key key; + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Warning: this example clears your mifare UID, use with care!")); + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } +} + +void loop() { + if ( mfrc522.MIFARE_UnbrickUidSector(false) ) { + Serial.println(F("Cleared sector 0, set UID to 1234. Card should be responsive again now.")); + } + delay(1000); +} diff --git a/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino b/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino index 36244a6..53b6e51 100644 --- a/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino +++ b/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino @@ -1,202 +1,319 @@ -/* - * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. - * The library file MFRC522.h has a wealth of useful info. Please read it. - * The functions are documented in MFRC522.cpp. - * - * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) - * Created by Miguel Balboa (circuitito.com), Jan, 2012. - * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) - * Released into the public domain. - * - * This sample shows how to setup a block on a MIFARE Classic PICC to be in "Value Block" mode. - * In Value Block mode the operations Increment/Decrement/Restore and Transfer can be used. +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * This sample shows how to setup blocks on a MIFARE Classic PICC (= card/tag) + * to be in "Value Block" mode: in this mode the operations Increment/Decrement, + * Restore and Transfer can be used. + * + * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 * - ----------------------------------------------------------------------------- empty_skull - * Aggiunti pin per arduino Mega - * add pin configuration for arduino mega - * http://mac86project.altervista.org/ - ----------------------------------------------------------------------------- Nicola Coppola - * Pin layout should be as follows: - * Signal Pin Pin Pin - * Arduino Uno Arduino Mega MFRC522 board - * ------------------------------------------------------------ - * Reset 9 5 RST - * SPI SS 10 53 SDA - * SPI MOSI 11 51 MOSI - * SPI MISO 12 50 MISO - * SPI SCK 13 52 SCK - * - * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. */ #include #include -#define SS_PIN 10 -#define RST_PIN 9 -MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +MFRC522::MIFARE_Key key; + +/** + * Initialize. + */ void setup() { - Serial.begin(9600); // Initialize serial communications with the PC - SPI.begin(); // Init SPI bus - mfrc522.PCD_Init(); // Init MFRC522 card - Serial.println("Scan a MIFARE Classic PICC to demonstrate Value Blocks."); + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + + // Prepare the key (used both as key A and as key B) + // using FFFFFFFFFFFFh which is the default at chip delivery from the factory + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + Serial.println(F("Scan a MIFARE Classic PICC to demonstrate Value Block mode.")); + Serial.print(F("Using key (for A and B):")); + dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + + Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1")); } +/** + * Main loop. + */ void loop() { - // Look for new cards - if ( ! mfrc522.PICC_IsNewCardPresent()) { - return; - } + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; - // Select one of the cards - if ( ! mfrc522.PICC_ReadCardSerial()) { - return; - } - // Now a card is selected. The UID and SAK is in mfrc522.uid. - - // Dump UID - Serial.print("Card UID:"); - for (byte i = 0; i < mfrc522.uid.size; i++) { - Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); - Serial.print(mfrc522.uid.uidByte[i], HEX); - } - Serial.println(); + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; - // Dump PICC type - byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - Serial.print("PICC type: "); - Serial.println(mfrc522.PICC_GetTypeName(piccType)); - if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI - && piccType != MFRC522::PICC_TYPE_MIFARE_1K - && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { - Serial.println("This sample only works with MIFARE Classic cards."); - return; - } + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); - // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. - MFRC522::MIFARE_Key key; - for (byte i = 0; i < 6; i++) { - key.keyByte[i] = 0xFF; - } + // Check for compatibility + if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI + && piccType != MFRC522::PICC_TYPE_MIFARE_1K + && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { + Serial.println(F("This sample only works with MIFARE Classic cards.")); + return; + } - // In this sample we use the second sector (ie block 4-7). - byte sector = 1; - byte valueBlockA = 5; - byte valueBlockB = 6; - byte trailerBlock = 7; - - // Authenticate using key A. - Serial.println("Authenticating using key A..."); - byte status; - status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - if (status != MFRC522::STATUS_OK) { - Serial.print("PCD_Authenticate() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - - // We need a sector trailer that defines blocks 5 and 6 as Value Blocks and enables key B. - byte trailerBuffer[] = { 255,255,255,255,255,255, 0,0,0, 0, 255,255,255,255,255,255}; // Keep default keys. - // g1=6 => Set block 5 as value block. Must use Key B towrite & increment, A or B can be used for derement. - // g2=6 => Same thing for block 6. - // g3=3 => Key B must be used to modify the Sector Trailer. Key B becomes valid. - mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); - - // Now we read the sector trailer and see if it is like we want it to be. - Serial.println("Reading sector trailer..."); - byte buffer[18]; - byte size = sizeof(buffer); - status = mfrc522.MIFARE_Read(trailerBlock, buffer, &size); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Read() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - if ( buffer[6] != trailerBuffer[6] - && buffer[7] != trailerBuffer[7] - && buffer[8] != trailerBuffer[8]) { - Serial.println("Writing new sector trailer..."); - status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Write() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - } - - // Authenticate using key B. - Serial.println("Authenticating again using key B..."); - status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); - if (status != MFRC522::STATUS_OK) { - Serial.print("PCD_Authenticate() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - - // Value blocks has a 32 bit signed value stored three times and an 8 bit address stored 4 times. - // Make sure blocks valueBlockA and valueBlockB has that format. - formatBlock(valueBlockA); - formatBlock(valueBlockB); + // In this sample we use the second sector, + // that is: sector #1, covering block #4 up to and including block #7 + byte sector = 1; + byte valueBlockA = 5; + byte valueBlockB = 6; + byte trailerBlock = 7; + MFRC522::StatusCode status; + byte buffer[18]; + byte size = sizeof(buffer); + long value; - // Add 1 to the value of valueBlockA and store the result in valueBlockA. - Serial.print("Adding 1 to value of block "); Serial.println(valueBlockA); - status = mfrc522.MIFARE_Increment(valueBlockA, 1); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Increment() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - status = mfrc522.MIFARE_Transfer(valueBlockA); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Transfer() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - - // Dump the result - mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); - - // Halt PICC - mfrc522.PICC_HaltA(); + // Authenticate using key A + Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } - // Stop encryption on PCD - mfrc522.PCD_StopCrypto1(); + // Show the whole sector as it currently is + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // We need a sector trailer that defines blocks 5 and 6 as Value Blocks and enables key B + // The last block in a sector (block #3 for Mifare Classic 1K) is the Sector Trailer. + // See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + // Bytes 0-5: Key A + // Bytes 6-8: Access Bits + // Bytes 9: User data + // Bytes 10-15: Key B (or user data) + byte trailerBuffer[] = { + 255, 255, 255, 255, 255, 255, // Keep default key A + 0, 0, 0, + 0, + 255, 255, 255, 255, 255, 255}; // Keep default key B + // The access bits are stored in a peculiar fashion. + // There are four groups: + // g[0] Access bits for block 0 (for sectors 0-31) + // or blocks 0-4 (for sectors 32-39) + // g[1] Access bits for block 1 (for sectors 0-31) + // or blocks 5-9 (for sectors 32-39) + // g[2] Access bits for block 2 (for sectors 0-31) + // or blocks 10-14 (for sectors 32-39) + // g[3] Access bits for the Sector Trailer: block 3 (for sectors 0-31) + // or block 15 (for sectors 32-39) + // Each group has access bits [C1 C2 C3], in this code C1 is MSB and C3 is LSB. + // Determine the bit pattern needed using MIFARE_SetAccessBits: + // g0=0 access bits for block 0 (of this sector) using [0 0 0] = 000b = 0 + // which means key A|B have r/w for block 0 of this sector + // which (in this example) translates to block #4 within sector #1; + // this is the transport configuration (at factory delivery). + // g1=6 access bits for block 1 (of this sector) using [1 1 0] = 110b = 6 + // which means block 1 (of this sector) is used as a value block, + // which (in this example) translates to block #5 within sector #1; + // where key A|B have r, key B has w, key B can increment, + // and key A|B can decrement, transfer, and restore. + // g2=6 same thing for block 2 (of this sector): set it to a value block; + // which (in this example) translates to block #6 within sector #1; + // g3=3 access bits for block 3 (of this sector): the Sector Trailer here; + // using [0 1 1] = 011b = 3 which means only key B has r/w access + // to the Sector Trailer (block 3 of this sector) from now on + // which (in this example) translates to block #7 within sector #1; + mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); + + // Read the sector trailer as it is currently stored on the PICC + Serial.println(F("Reading sector trailer...")); + status = mfrc522.MIFARE_Read(trailerBlock, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Check if it matches the desired access pattern already; + // because if it does, we don't need to write it again... + if ( buffer[6] != trailerBuffer[6] + && buffer[7] != trailerBuffer[7] + && buffer[8] != trailerBuffer[8]) { + // They don't match (yet), so write it to the PICC + Serial.println(F("Writing new sector trailer...")); + status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + } + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // A value block has a 32 bit signed value stored three times + // and an 8 bit address stored 4 times. Make sure that valueBlockA + // and valueBlockB have that format (note that it will only format + // the block when it doesn't comply to the expected format already). + formatValueBlock(valueBlockA); + formatValueBlock(valueBlockB); + + // Add 1 to the value of valueBlockA and store the result in valueBlockA. + Serial.print("Adding 1 to value of block "); Serial.println(valueBlockA); + status = mfrc522.MIFARE_Increment(valueBlockA, 1); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Increment() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + status = mfrc522.MIFARE_Transfer(valueBlockA); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Transfer() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Show the new value of valueBlockA + status = mfrc522.MIFARE_GetValue(valueBlockA, &value); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_GetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + Serial.print("New value of value block "); Serial.print(valueBlockA); + Serial.print(" = "); Serial.println(value); + + // Decrement 10 from the value of valueBlockB and store the result in valueBlockB. + Serial.print("Subtracting 10 from value of block "); Serial.println(valueBlockB); + status = mfrc522.MIFARE_Decrement(valueBlockB, 10); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Decrement() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + status = mfrc522.MIFARE_Transfer(valueBlockB); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Transfer() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Show the new value of valueBlockB + status = mfrc522.MIFARE_GetValue(valueBlockB, &value); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_GetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + Serial.print(F("New value of value block ")); Serial.print(valueBlockB); + Serial.print(F(" = ")); Serial.println(value); + // Check some boundary... + if (value <= -100) { + Serial.println(F("Below -100, so resetting it to 255 = 0xFF just for fun...")); + status = mfrc522.MIFARE_SetValue(valueBlockB, 255); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_SetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + } + + // Dump the sector data + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Halt PICC + mfrc522.PICC_HaltA(); + // Stop encryption on PCD + mfrc522.PCD_StopCrypto1(); } -void formatBlock(byte blockAddr) { - Serial.print("Reading block "); Serial.println(blockAddr); - byte buffer[18]; - byte size = sizeof(buffer); - byte status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Read() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} - if ( (buffer[0] == (byte)~buffer[4]) - && (buffer[1] == (byte)~buffer[5]) - && (buffer[2] == (byte)~buffer[6]) - && (buffer[3] == (byte)~buffer[7]) - - && (buffer[0] == buffer[8]) - && (buffer[1] == buffer[9]) - && (buffer[2] == buffer[10]) - && (buffer[3] == buffer[11]) - - && (buffer[12] == (byte)~buffer[13]) - && (buffer[12] == buffer[14]) - && (buffer[12] == (byte)~buffer[15])) { - Serial.println("Block has correct Block Value format."); - } - else { - Serial.println("Writing new value block..."); - byte valueBlock[] = { 0,0,0,0, 255,255,255,255, 0,0,0,0, blockAddr,~blockAddr,blockAddr,~blockAddr }; - status = mfrc522.MIFARE_Write(blockAddr, valueBlock, 16); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Write() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - } - } -} // End formatBlock() +/** + * Ensure that a given block is formatted as a Value Block. + */ +void formatValueBlock(byte blockAddr) { + byte buffer[18]; + byte size = sizeof(buffer); + MFRC522::StatusCode status; + + Serial.print(F("Reading block ")); Serial.println(blockAddr); + status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + if ( (buffer[0] == (byte)~buffer[4]) + && (buffer[1] == (byte)~buffer[5]) + && (buffer[2] == (byte)~buffer[6]) + && (buffer[3] == (byte)~buffer[7]) + + && (buffer[0] == buffer[8]) + && (buffer[1] == buffer[9]) + && (buffer[2] == buffer[10]) + && (buffer[3] == buffer[11]) + + && (buffer[12] == (byte)~buffer[13]) + && (buffer[12] == buffer[14]) + && (buffer[12] == (byte)~buffer[15])) { + Serial.println(F("Block has correct Value Block format.")); + } + else { + Serial.println(F("Formatting as Value Block...")); + byte valueBlock[] = { + 0, 0, 0, 0, + 255, 255, 255, 255, + 0, 0, 0, 0, + blockAddr, ~blockAddr, blockAddr, ~blockAddr }; + status = mfrc522.MIFARE_Write(blockAddr, valueBlock, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + } +} diff --git a/examples/ReadAndWrite/ReadAndWrite.ino b/examples/ReadAndWrite/ReadAndWrite.ino index 89d96a6..58400f6 100644 --- a/examples/ReadAndWrite/ReadAndWrite.ino +++ b/examples/ReadAndWrite/ReadAndWrite.ino @@ -1,240 +1,200 @@ -/* - * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. - * The library file MFRC522.h has a wealth of useful info. Please read it. - * The functions are documented in MFRC522.cpp. - * - * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) - * Created by Miguel Balboa (circuitito.com), Jan, 2012. - * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. * * Released into the public domain. - * - * This sample shows how to setup a block on a MIFARE Classic PICC to be in "Value Block" mode. - * In Value Block mode the operations Increment/Decrement/Restore and Transfer can be used. + * ---------------------------------------------------------------------------- + * This sample shows how to read and write data blocks on a MIFARE Classic PICC + * (= card/tag). + * + * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 * - ----------------------------------------------------------------------------- empty_skull - - - Aggiunti pin per arduino Mega - - Scritto semplice codice per la scrittura e lettura - - - add pin configuration for arduino mega - - write simple read/write Code for new entry user - - http://mac86project.altervista.org/ - - ----------------------------------------------------------------------------- Nicola Coppola - * Pin layout should be as follows: - * Signal Pin Pin Pin - * Arduino Uno Arduino Mega MFRC522 board - * ------------------------------------------------------------ - * Reset 9 5 RST - * SPI SS 10 53 SDA - * SPI MOSI 11 51 MOSI - * SPI MISO 12 50 MISO - * SPI SCK 13 52 SCK - * - * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. */ #include #include -#define SS_PIN 53 -#define RST_PIN 5 -MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +MFRC522::MIFARE_Key key; + +/** + * Initialize. + */ void setup() { - Serial.begin(9600); // Initialize serial communications with the PC - SPI.begin(); // Init SPI bus - mfrc522.PCD_Init(); // Init MFRC522 card - //Serial.println("Scan a MIFARE Classic PICC to demonstrate Value Blocks."); + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + + // Prepare the key (used both as key A and as key B) + // using FFFFFFFFFFFFh which is the default at chip delivery from the factory + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write.")); + Serial.print(F("Using key (for A and B):")); + dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + + Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1")); } +/** + * Main loop. + */ void loop() { - - // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. - MFRC522::MIFARE_Key key; - for (byte i = 0; i < 6; i++) { - key.keyByte[i] = 0xFF; - } - // Look for new cards - if ( ! mfrc522.PICC_IsNewCardPresent()) { - return; - } + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; - // Select one of the cards - if ( ! mfrc522.PICC_ReadCardSerial()) { - return; - } - // Now a card is selected. The UID and SAK is in mfrc522.uid. - - // Dump UID - Serial.print("Card UID:"); - for (byte i = 0; i < mfrc522.uid.size; i++) { - Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); - Serial.print(mfrc522.uid.uidByte[i], HEX); - } - Serial.println(); + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; - // Dump PICC type - byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); - Serial.print("PICC type: "); - Serial.println(mfrc522.PICC_GetTypeName(piccType)); - if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI - && piccType != MFRC522::PICC_TYPE_MIFARE_1K - && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { - //Serial.println("This sample only works with MIFARE Classic cards."); - return; - } - - // In this sample we use the second sector (ie block 4-7). the first sector is = 0 - // scegliere settore di lettura da 0 = primo settore - byte sector = 1; - // block sector 0-3(sector0) 4-7(sector1) 8-11(sector2) - // blocchi di scrittura da 0-3(sector0) 4-7(sector1) 8-11(sector2) - byte valueBlockA = 4; - byte valueBlockB = 5; - byte valueBlockC = 6; - byte trailerBlock = 7; - byte status; - // Authenticate using key A. - // avvio l'autentificazione A - //Serial.println("Authenticating using key A..."); - status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); - if (status != MFRC522::STATUS_OK) { - Serial.print("PCD_Authenticate() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - // Authenticate using key B. - // avvio l'autentificazione B - //Serial.println("Authenticating again using key B..."); - status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); - if (status != MFRC522::STATUS_OK) { - Serial.print("PCD_Authenticate() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - return; - } - - // Writing new value block A - // Scrivo i valori per il settore A - Serial.println("Writing new value block A(4) : the first of the sector TWO "); - byte value1Block[] = { 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA }; - status = mfrc522.MIFARE_Write(valueBlockA, value1Block, 16); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Write() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - } - /* - - // Writing new value block B - // Scrivo i valori per il settore B - Serial.println("Writing new value block B"); - byte value2Block[] = { 255,255,255,255, 0,0,0,0, 0,0,0,0, 255,255,255,255, valueBlockB,~valueBlockB,valueBlockB,~valueBlockB }; - status = mfrc522.MIFARE_Write(valueBlockB, value2Block, 16); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Write() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - } - - // Writing new value block D - // Scrivo i valori per il settore C - Serial.println("Writing new value block C"); - byte value3Block[] = { 255,255,255,255, 0,0,0,0, 0,0,0,0, 255,255,255,255, valueBlockC,~valueBlockC,valueBlockC,~valueBlockC }; - status = mfrc522.MIFARE_Write(valueBlockC, value3Block, 16); - if (status != MFRC522::STATUS_OK) { - Serial.print("MIFARE_Write() failed: "); - Serial.println(mfrc522.GetStatusCodeName(status)); - } - - */ - - - Serial.println("Read block A(4) : the first of the sector TWO"); - byte buffer[18]; - byte size = sizeof(buffer); - // change this: valueBlockA , for read anather block - // cambiate valueBlockA per leggere un altro blocco - status = mfrc522.MIFARE_Read(valueBlockA, buffer, &size); - Serial.print("Settore : 0 Valore :"); - Serial.println(buffer[0]); - Serial.print("Settore : 1 Valore :"); - Serial.println(buffer[1]); - Serial.print("Settore : 2 Valore :"); - Serial.println(buffer[2]); - Serial.print("Settore : 3 Valore :"); - Serial.println(buffer[3]); - Serial.print("Settore : 4 Valore :"); - Serial.println(buffer[4]); - Serial.print("Settore : 5 Valore :"); - Serial.println(buffer[5]); - Serial.print("Settore : 6 Valore :"); - Serial.println(buffer[6]); - Serial.print("Settore : 7 Valore :"); - Serial.println(buffer[7]); - Serial.print("Settore : 8 Valore :"); - Serial.println(buffer[8]); - Serial.print("Settore : 9 Valore :"); - Serial.println(buffer[9]); - Serial.print("Settore :10 Valore :"); - Serial.println(buffer[10]); - Serial.print("Settore :11 Valore :"); - Serial.println(buffer[11]); - Serial.print("Settore :12 Valore :"); - Serial.println(buffer[12]); - Serial.print("Settore :13 Valore :"); - Serial.println(buffer[13]); - Serial.print("Settore :14 Valore :"); - Serial.println(buffer[14]); - Serial.print("Settore :15 Valore :"); - Serial.println(buffer[15]); - - - - //byte value1Block[] = { 1,2,3,4, 5,6,7,8, 9,10,255,12, 13,14,15,16, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA }; - if ( - buffer[0] == 1 && - buffer[1] == 2 && - buffer[2] == 3 && - buffer[3] == 4 && - buffer[4] == 5 && - buffer[5] == 6 && - buffer[6] == 7 && - buffer[7] == 8 && - buffer[8] == 9 && - buffer[9] == 10 && - buffer[10] == 255 && - buffer[11] == 12 && - buffer[12] == 13 && - buffer[13] == 14 && - buffer[14] == 15 && - buffer[15] == 16 - ){ - - // sel a scrittura è uguale alla lettura allora e stato un successo !! - Serial.println("Read block A(4) : the first of the sector TWO : success"); - Serial.println(":-)"); - - }else{ - - // scrittura Fallita - Serial.println("Read block A(4) : the first of the sector TWO : no match - write don't work fine "); - Serial.println(":-( "); - - } - - - // risponde successo - //Serial.println(mfrc522.GetStatusCodeName(status)); - - // Dump the result - //mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); - - // Halt PICC - mfrc522.PICC_HaltA(); + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); - // Stop encryption on PCD - mfrc522.PCD_StopCrypto1(); + // Check for compatibility + if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI + && piccType != MFRC522::PICC_TYPE_MIFARE_1K + && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { + Serial.println(F("This sample only works with MIFARE Classic cards.")); + return; + } + + // In this sample we use the second sector, + // that is: sector #1, covering block #4 up to and including block #7 + byte sector = 1; + byte blockAddr = 4; + byte dataBlock[] = { + 0x01, 0x02, 0x03, 0x04, // 1, 2, 3, 4, + 0x05, 0x06, 0x07, 0x08, // 5, 6, 7, 8, + 0x08, 0x09, 0xff, 0x0b, // 9, 10, 255, 12, + 0x0c, 0x0d, 0x0e, 0x0f // 13, 14, 15, 16 + }; + byte trailerBlock = 7; + MFRC522::StatusCode status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Authenticate using key A + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Show the whole sector as it currently is + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Read data from the block + Serial.print(F("Reading data from block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":")); + dump_byte_array(buffer, 16); Serial.println(); + Serial.println(); + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write data to the block + Serial.print(F("Writing data into block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + dump_byte_array(dataBlock, 16); Serial.println(); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.println(); + + // Read data from the block (again, should now be what we have written) + Serial.print(F("Reading data from block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":")); + dump_byte_array(buffer, 16); Serial.println(); + // Check that data in block is what we have written + // by counting the number of bytes that are equal + Serial.println(F("Checking result...")); + byte count = 0; + for (byte i = 0; i < 16; i++) { + // Compare buffer (= what we've read) with dataBlock (= what we've written) + if (buffer[i] == dataBlock[i]) + count++; + } + Serial.print(F("Number of bytes that match = ")); Serial.println(count); + if (count == 16) { + Serial.println(F("Success :-)")); + } else { + Serial.println(F("Failure, no match :-(")); + Serial.println(F(" perhaps the write didn't work properly...")); + } + Serial.println(); + // Dump the sector data + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Halt PICC + mfrc522.PICC_HaltA(); + // Stop encryption on PCD + mfrc522.PCD_StopCrypto1(); +} + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } } diff --git a/examples/ReadUidMultiReader/ReadUidMultiReader.ino b/examples/ReadUidMultiReader/ReadUidMultiReader.ino new file mode 100644 index 0000000..1c40be1 --- /dev/null +++ b/examples/ReadUidMultiReader/ReadUidMultiReader.ino @@ -0,0 +1,94 @@ +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * This sample shows how to read and write data blocks on a MIFARE Classic PICC + * (= card/tag). + * + * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS 1 SDA(SS) 10 53 D10 10 10 + * SPI SS 2 SDA(SS) 2 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + */ + +#include +#include + +#define RST_PIN 10 // Configurable, see typical pin layout above +#define SS_1_PIN 5 // Configurable, see typical pin layout above +#define SS_2_PIN 3 // Configurable, see typical pin layout above + +#define NR_OF_READERS 2 + +byte ssPins[] = {SS_1_PIN, SS_2_PIN}; + +MFRC522 mfrc522[NR_OF_READERS]; // Create MFRC522 instance. + +/** + * Initialize. + */ +void setup() { + + Serial.begin(115200); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + + SPI.begin(); // Init SPI bus + + for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) { + mfrc522[reader].PCD_Init(ssPins[reader], RST_PIN); // Init each MFRC522 card + } +} + +/** + * Main loop. + */ +void loop() { + + for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) { + // Look for new cards + + if (mfrc522[reader].PICC_IsNewCardPresent() && mfrc522[reader].PICC_ReadCardSerial()) { + Serial.print(F("Reader: ")); + Serial.print(reader); + // Show some details of the PICC (that is: the tag/card) + Serial.print(F(" Card UID:")); + dump_byte_array(mfrc522[reader].uid.uidByte, mfrc522[reader].uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + byte piccType = mfrc522[reader].PICC_GetType(mfrc522[reader].uid.sak); + Serial.println(mfrc522[reader].PICC_GetTypeName(piccType)); + + // Halt PICC + mfrc522[reader].PICC_HaltA(); + // Stop encryption on PCD + mfrc522[reader].PCD_StopCrypto1(); + } //if (mfrc522[reader].PICC_IsNewC + } //for(uint8_t reader +} + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} diff --git a/examples/firmware_check/firmware_check.ino b/examples/firmware_check/firmware_check.ino new file mode 100644 index 0000000..b923074 --- /dev/null +++ b/examples/firmware_check/firmware_check.ino @@ -0,0 +1,81 @@ +/* + * ---------------------------------------------------------------------------- + * Example sketch/program to test your firmware. + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. The functions are + * documented in MFRC522.cpp. Please read it. + * + * Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * @author Rotzbua + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 module + + Serial.println(F("*****************************")); + Serial.println(F("MFRC522 Digital self test")); + Serial.println(F("*****************************")); + ShowReaderVersion(); // Show version of PCD - MFRC522 Card Reader + Serial.println(F("Performing test...")); + bool result = mfrc522.PCD_PerformSelfTest(); + Serial.println(F("-----------------------------")); + Serial.print(F("Result: ")); + if (result) + Serial.println(F("OK")); + else + Serial.println(F("DEFECT or UNKNOWN")); + Serial.println(); +} + +void loop() {} // nothing to do + +/** +* Helper to print MFRC522 module info +*/ +void ShowReaderVersion() { + // Get the MFRC522 firmware version + byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); + Serial.print(F("Firmware Version: 0x")); + Serial.print(v, HEX); + if (v == 0x88) + Serial.print(F(" = (clone)")); + else if (v == 0x90) + Serial.print(F(" = v0.0")); + else if (v == 0x91) + Serial.print(F(" = v1.0")); + else if (v == 0x92) + Serial.print(F(" = v2.0")); + else + Serial.print(F(" = (unknown)")); + Serial.println(); + // When 0x00 or 0xFF is returned, communication probably failed + if ((v == 0x00) || (v == 0xFF)) + Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?")); +} diff --git a/examples/rfid_default_keys/rfid_default_keys.ino b/examples/rfid_default_keys/rfid_default_keys.ino new file mode 100644 index 0000000..32fb6c5 --- /dev/null +++ b/examples/rfid_default_keys/rfid_default_keys.ino @@ -0,0 +1,152 @@ +/* + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * Example sketch/program which will try the most used default keys listed in + * https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys to dump the + * block 0 of a MIFARE RFID card using a RFID-RC522 reader. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +// Number of known default keys (hard-coded) +// NOTE: Synchronize the NR_KNOWN_KEYS define with the defaultKeys[] array +#define NR_KNOWN_KEYS 8 +// Known keys, see: https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys +byte knownKeys[NR_KNOWN_KEYS][MFRC522::MF_KEY_SIZE] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FF FF FF FF FF FF = factory default + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // A0 A1 A2 A3 A4 A5 + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, // B0 B1 B2 B3 B4 B5 + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, // 4D 3A 99 C3 51 DD + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, // 1A 98 2C 7E 45 9A + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // D3 F7 D3 F7 D3 F7 + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, // AA BB CC DD EE FF + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // 00 00 00 00 00 00 +}; + +/* + * Initialize. + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Try the most used default keys to print block 0 of a MIFARE PICC.")); +} + +/* + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} + +/* + * Try using the PICC (the tag/card) with the given key to access block 0. + * On success, it will show the key details, and dump the block data on Serial. + * + * @return true when the given key worked, false otherwise. + */ +boolean try_key(MFRC522::MIFARE_Key *key) +{ + boolean result = false; + byte buffer[18]; + byte block = 0; + MFRC522::StatusCode status; + + // Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + // Serial.print(F("PCD_Authenticate() failed: ")); + // Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Read block + byte byteCount = sizeof(buffer); + status = mfrc522.MIFARE_Read(block, buffer, &byteCount); + if (status != MFRC522::STATUS_OK) { + // Serial.print(F("MIFARE_Read() failed: ")); + // Serial.println(mfrc522.GetStatusCodeName(status)); + } + else { + // Successful read + result = true; + Serial.print(F("Success with key:")); + dump_byte_array((*key).keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + // Dump block data + Serial.print(F("Block ")); Serial.print(block); Serial.print(F(":")); + dump_byte_array(buffer, 16); + Serial.println(); + } + Serial.println(); + + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + return result; +} + +/* + * Main loop. + */ +void loop() { + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Try the known default keys + MFRC522::MIFARE_Key key; + for (byte k = 0; k < NR_KNOWN_KEYS; k++) { + // Copy the known key into the MIFARE_Key structure + for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { + key.keyByte[i] = knownKeys[k][i]; + } + // Try the key + if (try_key(&key)) { + // Found and reported on the key and block, + // no need to try other keys for this PICC + break; + } + } +} diff --git a/examples/rfid_write_personal_data/rfid_write_personal_data.ino b/examples/rfid_write_personal_data/rfid_write_personal_data.ino new file mode 100644 index 0000000..ffbec07 --- /dev/null +++ b/examples/rfid_write_personal_data/rfid_write_personal_data.ino @@ -0,0 +1,153 @@ +/* + * Write personal data of a MIFARE RFID card using a RFID-RC522 reader + * Uses MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * Hardware required: + * Arduino + * PCD (Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * PICC (Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. + */ + +#include +#include + +#define SS_PIN 10 //Arduino Uno +#define RST_PIN 9 +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Write personal data on a MIFARE PICC ")); +} + +void loop() { + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + MFRC522::MIFARE_Key key; + for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; + + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) return; + + Serial.print(F("Card UID:")); //Dump UID + for (byte i = 0; i < mfrc522.uid.size; i++) { + Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); + Serial.print(mfrc522.uid.uidByte[i], HEX); + } + Serial.print(F(" PICC type: ")); // Dump PICC type + byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + byte buffer[34]; + byte block; + MFRC522::StatusCode status; + byte len; + + Serial.setTimeout(20000L) ; // wait until 20 seconds for input from serial + // Ask personal data: Family name + Serial.println(F("Type Family name, ending with #")); + len=Serial.readBytesUntil('#', (char *) buffer, 30) ; // read family name from serial + for (byte i = len; i < 30; i++) buffer[i] = ' '; // pad with spaces + + block = 1; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("PCD_Authenticate() success: ")); + + // Write block + status = mfrc522.MIFARE_Write(block, buffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + block = 2; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, &buffer[16], 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + // Ask personal data: First name + Serial.println(F("Type First name, ending with #")); + len=Serial.readBytesUntil('#', (char *) buffer, 20) ; // read first name from serial + for (byte i = len; i < 20; i++) buffer[i] = ' '; // pad with spaces + + block = 4; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, buffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + block = 5; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, &buffer[16], 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + + Serial.println(" "); + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + +} diff --git a/keywords.txt b/keywords.txt index d67a600..73e6216 100644 --- a/keywords.txt +++ b/keywords.txt @@ -8,6 +8,7 @@ MFRC522 KEYWORD1 PCD_Register KEYWORD1 PCD_Command KEYWORD1 +PCD_RxGain KEYWORD1 PICC_Command KEYWORD1 MIFARE_Misc KEYWORD1 PICC_Type KEYWORD1 @@ -18,7 +19,10 @@ MIFARE_Key KEYWORD1 ####################################### # KEYWORD2 Methods and functions ####################################### +# Functions for setting up the Arduino setSPIConfig KEYWORD2 + +# Basic interface functions for communicating with the MFRC522 PCD_WriteRegister KEYWORD2 PCD_WriteRegister KEYWORD2 PCD_ReadRegister KEYWORD2 @@ -27,9 +31,17 @@ setBitMask KEYWORD2 PCD_SetRegisterBitMask KEYWORD2 PCD_ClearRegisterBitMask KEYWORD2 PCD_CalculateCRC KEYWORD2 + +# Functions for manipulating the MFRC522 PCD_Init KEYWORD2 PCD_Reset KEYWORD2 PCD_AntennaOn KEYWORD2 +PCD_AntennaOff KEYWORD2 +PCD_GetAntennaGain KEYWORD2 +PCD_SetAntennaGain KEYWORD2 +PCD_PerformSelfTest KEYWORD2 + +# Functions for communicating with PICCs PCD_TransceiveData KEYWORD2 PCD_CommunicateWithPICC KEYWORD2 PICC_RequestA KEYWORD2 @@ -37,19 +49,32 @@ PICC_WakeupA KEYWORD2 PICC_REQA_or_WUPA KEYWORD2 PICC_Select KEYWORD2 PICC_HaltA KEYWORD2 + +# Functions for communicating with MIFARE PICCs PCD_Authenticate KEYWORD2 PCD_StopCrypto1 KEYWORD2 MIFARE_Read KEYWORD2 MIFARE_Write KEYWORD2 MIFARE_Increment KEYWORD2 MIFARE_Ultralight_Write KEYWORD2 +MIFARE_GetValue KEYWORD2 +MIFARE_SetValue KEYWORD2 + +# Support functions PCD_MIFARE_Transceive KEYWORD2 +GetStatusCodeName KEYWORD2 PICC_GetType KEYWORD2 +PICC_GetTypeName KEYWORD2 PICC_DumpToSerial KEYWORD2 PICC_DumpMifareClassicToSerial KEYWORD2 PICC_DumpMifareClassicSectorToSerial KEYWORD2 PICC_DumpMifareUltralightToSerial KEYWORD2 MIFARE_SetAccessBits KEYWORD2 +MIFARE_OpenUidBackdoor KEYWORD2 +MIFARE_SetUid KEYWORD2 +MIFARE_UnbrickUidSector KEYWORD2 + +# Convenience functions - does not add extra functionality PICC_IsNewCardPresent KEYWORD2 PICC_ReadCardSerial KEYWORD2 @@ -120,6 +145,17 @@ PCD_Receive LITERAL1 PCD_Transceive LITERAL1 PCD_MFAuthent LITERAL1 PCD_SoftReset LITERAL1 +RxGain_18dB LITERAL1 +RxGain_23dB LITERAL1 +RxGain_18dB_2 LITERAL1 +RxGain_23dB_2 LITERAL1 +RxGain_33dB LITERAL1 +RxGain_38dB LITERAL1 +RxGain_43dB LITERAL1 +RxGain_48dB LITERAL1 +RxGain_min LITERAL1 +RxGain_avg LITERAL1 +RxGain_max LITERAL1 PICC_CMD_REQA LITERAL1 PICC_CMD_WUPA LITERAL1 PICC_CMD_CT LITERAL1 @@ -158,4 +194,3 @@ STATUS_INVALID LITERAL1 STATUS_CRC_WRONG LITERAL1 STATUS_MIFARE_NACK LITERAL1 FIFO_SIZE LITERAL1 - diff --git a/library.json b/library.json new file mode 100644 index 0000000..94e8453 --- /dev/null +++ b/library.json @@ -0,0 +1,13 @@ +{ + "name": "MFRC522", + "keywords": "rfid, spi", + "description": "Read a card using a MFRC522 reader on your SPI interface", + "repository": + { + "type": "git", + "url": "https://github.com/miguelbalboa/rfid.git" + }, + "exclude": "doc", + "frameworks": "arduino", + "platforms": ["atmelavr", "ststm32"] +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..d223ed8 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=MFRC522 +#date as version - no leading zero +version=2015.9.4 +author=GithubCommunity +maintainer=miguelbalboa +sentence=Arduino RFID Library for MFRC522 (SPI) +paragraph=Read/Write a RFID Card or Tag using the ISO/IEC 14443A/MIFARE interface. +category=Communication +url=https://github.com/miguelbalboa/rfid +architectures=avr,STM32F1