diff --git a/MFRC522.cpp b/MFRC522.cpp index 8b20c0c..88d7478 100644 --- a/MFRC522.cpp +++ b/MFRC522.cpp @@ -100,7 +100,7 @@ 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 @@ -114,7 +114,7 @@ void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One o 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); } @@ -279,71 +279,69 @@ void MFRC522::PCD_SetAntennaGain(byte mask) { * @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 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; + // 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 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() ///////////////////////////////////////////////////////////////////////////////////// @@ -386,10 +384,10 @@ byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execut ) { 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 @@ -454,7 +452,7 @@ 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]; + byte controlBuffer[2]; n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); if (n != STATUS_OK) { return n; @@ -542,7 +540,7 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp bool uidComplete; bool selectDone; bool useCascadeTag; - byte cascadeLevel = 1; + byte cascadeLevel = 1; byte result; byte count; byte index; @@ -581,13 +579,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. - + // 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: @@ -626,7 +624,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++) { @@ -640,10 +638,10 @@ 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 // Calculate BCC - Block Check Character buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; @@ -659,7 +657,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 @@ -669,13 +667,13 @@ 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 @@ -692,7 +690,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; @@ -708,8 +706,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[] @@ -720,7 +718,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. @@ -738,11 +736,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() @@ -753,8 +751,8 @@ byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally outp */ byte MFRC522::PICC_HaltA() { byte result; - byte buffer[4]; - + byte buffer[4]; + // Build command buffer buffer[0] = PICC_CMD_HLTA; buffer[1] = 0; @@ -763,7 +761,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 @@ -853,7 +851,7 @@ byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0x if (buffer == NULL || *bufferSize < 18) { return STATUS_NO_ROOM; } - + // Build command buffer buffer[0] = PICC_CMD_MF_READ; buffer[1] = blockAddr; @@ -883,12 +881,12 @@ byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0x byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. ) { byte result; - + // Sanity check if (buffer == NULL || bufferSize < 16) { return STATUS_INVALID; } - + // Mifare Classic protocol requires two communications to perform a write. // Step 1: Tell the PICC we want to write to block blockAddr. byte cmdBuffer[2]; @@ -898,13 +896,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() @@ -918,12 +916,12 @@ byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to writ byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. ) { byte result; - + // Sanity check if (buffer == NULL || bufferSize < 4) { return STATUS_INVALID; } - + // Build commmand buffer byte cmdBuffer[6]; cmdBuffer[0] = PICC_CMD_UL_WRITE; @@ -992,7 +990,7 @@ byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use ) { byte result; byte cmdBuffer[2]; // We only need room for 2 bytes. - + // Step 1: Tell the PICC the command and block address cmdBuffer[0] = command; cmdBuffer[1] = blockAddr; @@ -1000,13 +998,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() @@ -1021,7 +1019,7 @@ byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. ) { byte result; byte cmdBuffer[2]; // We only need room for 2 bytes. - + // Tell the PICC we want to transfer the result into block blockAddr. cmdBuffer[0] = PICC_CMD_MF_TRANSFER; cmdBuffer[1] = blockAddr; @@ -1044,17 +1042,17 @@ byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. * @return STATUS_OK on success, STATUS_??? otherwise. */ byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { - byte status; - byte buffer[18]; - byte size = sizeof(buffer); - - // Read the block - status = MIFARE_Read(blockAddr, buffer, &size); - if (status == STATUS_OK) { - // Extract the value - *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); - } - return status; + byte status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Read the block + status = MIFARE_Read(blockAddr, buffer, &size); + if (status == STATUS_OK) { + // Extract the value + *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + } + return status; } // End MIFARE_GetValue() /** @@ -1069,24 +1067,24 @@ byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { * @return STATUS_OK on success, STATUS_??? otherwise. */ byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { - byte buffer[18]; - - // Translate the long into 4 bytes; repeated 2x in value block - buffer[0] = buffer[ 8] = (value & 0xFF); - buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; - buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; - buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; - // Inverse 4 bytes also found in value block - buffer[4] = ~buffer[0]; - buffer[5] = ~buffer[1]; - buffer[6] = ~buffer[2]; - buffer[7] = ~buffer[3]; - // Address 2x with inverse address 2x - buffer[12] = buffer[14] = blockAddr; - buffer[13] = buffer[15] = ~blockAddr; - - // Write the whole data block - return MIFARE_Write(blockAddr, buffer, 16); + 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() ///////////////////////////////////////////////////////////////////////////////////// @@ -1105,7 +1103,7 @@ byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data t ) { byte result; byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. - + // Sanity check if (sendData == NULL || sendLen > 16) { return STATUS_INVALID; @@ -1226,11 +1224,14 @@ void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned fro // 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(F("PICC type: ")); @@ -1252,7 +1253,7 @@ 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: @@ -1264,7 +1265,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() @@ -1295,7 +1296,7 @@ void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid str break; default: // Should not happen. Ignore. - break; + break; } // Dump sectors, highest address first. @@ -1322,7 +1323,7 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U 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) @@ -1337,7 +1338,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; @@ -1360,7 +1361,10 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U 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(F(" ")); } @@ -1368,7 +1372,14 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U 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(F(" ")); // Establish encrypted communications before reading the first block @@ -1390,7 +1401,10 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U } // 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(F(" ")); @@ -1411,7 +1425,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; @@ -1425,8 +1439,8 @@ void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to U if (firstInGroup) { // Print access bits Serial.print(F(" [ ")); - Serial.print((g[group] >> 2) & 1, DEC); Serial.print(" "); - Serial.print((g[group] >> 1) & 1, DEC); Serial.print(" "); + Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); Serial.print((g[group] >> 0) & 1, DEC); Serial.print(F(" ] ")); if (invertedError) { @@ -1453,7 +1467,7 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { byte byteCount; byte buffer[18]; byte i; - + Serial.println(F("Page 0 1 2 3")); // Try the mpages of the original Ultralight. Ultralight C has more pages. for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. @@ -1468,12 +1482,18 @@ void MFRC522::PICC_DumpMifareUltralightToSerial() { // 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(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(); @@ -1512,65 +1532,65 @@ void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte * Of course with non-bricked devices, you're free to select them before calling this function. */ bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { - // Magic sequence: - // > 50 00 57 CD (HALT + CRC) - // > 40 (7 bits only) - // < A (4 bits only) - // > 43 - // < A (4 bits only) - // Then you can write to sector 0 without authenticating - - PICC_HaltA(); // 50 00 57 CD - - byte cmd = 0x40; - byte validBits = 7; /* Our command is only 7 bits. After receiving card response, - this will contain amount of valid response bits. */ - byte response[32]; // Card's response is written here - byte received; - byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 - if( status != STATUS_OK ) { - if( logErrors ) { - Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); - Serial.print(F("Error name: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - if ( received != 1 || response[0] != 0x0A ) { - if ( logErrors ) { - Serial.print(F("Got bad response on backdoor 0x40 command: ")); - Serial.print(response[0], HEX); - Serial.print(F(" (")); - Serial.print(validBits); - Serial.print(F(" valid bits)\r\n")); - } - return false; - } - - cmd = 0x43; - validBits = 8; - status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 - if( status != STATUS_OK ) { - if( logErrors ) { - Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); - Serial.print(F("Error name: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - if ( received != 1 || response[0] != 0x0A ) { - if ( logErrors ) { - Serial.print(F("Got bad response on backdoor 0x43 command: ")); - Serial.print(response[0], HEX); - Serial.print(F(" (")); - Serial.print(validBits); - Serial.print(F(" valid bits)\r\n")); - } - return false; - } - - // You can now write to sector 0 without authenticating! - return true; + // Magic sequence: + // > 50 00 57 CD (HALT + CRC) + // > 40 (7 bits only) + // < A (4 bits only) + // > 43 + // < A (4 bits only) + // Then you can write to sector 0 without authenticating + + PICC_HaltA(); // 50 00 57 CD + + byte cmd = 0x40; + byte validBits = 7; /* Our command is only 7 bits. After receiving card response, + this will contain amount of valid response bits. */ + byte response[32]; // Card's response is written here + byte received; + byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x40 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + cmd = 0x43; + validBits = 8; + status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x43 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" valid bits)\r\n")); + } + return false; + } + + // You can now write to sector 0 without authenticating! + return true; } // End MIFARE_OpenUidBackdoor() /** @@ -1581,122 +1601,122 @@ bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { * It assumes a default KEY A of 0xFFFFFFFFFFFF. * Make sure to have selected the card before this function is called. */ -bool MFRC522::MIFARE_SetUid(byte* newUid, byte uidSize, bool logErrors) { - - // UID + BCC byte can not be larger than 16 together - if ( !newUid || !uidSize || uidSize > 15) { - if ( logErrors ) { - Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); - } - return false; - } - - // Authenticate for reading - MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); - if ( status != STATUS_OK ) { - - if ( status == STATUS_TIMEOUT ) { - // We get a read timeout if no card is selected yet, so let's select one - - // Wake the card up again if sleeping -// byte atqa_answer[2]; -// byte atqa_size = 2; -// PICC_WakeupA(atqa_answer, &atqa_size); - - if ( !PICC_IsNewCardPresent() || !PICC_ReadCardSerial() ) { - Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); - return false; - } - - status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); - if ( status != STATUS_OK ) { - // We tried, time to give up - if ( logErrors ) { - Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - } - else { - if ( logErrors ) { - Serial.print(F("PCD_Authenticate() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - } - - // Read block 0 - byte block0_buffer[18]; - byte byteCount = sizeof(block0_buffer); - status = MIFARE_Read((byte)0, block0_buffer, &byteCount); - if ( status != STATUS_OK ) { - if ( logErrors ) { - Serial.print(F("MIFARE_Read() failed: ")); - Serial.println(GetStatusCodeName(status)); - Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); - } - return false; - } - - // Write new UID to the data we just read, and calculate BCC byte - byte bcc = 0; - for ( int i = 0; i < uidSize; i++ ) { - block0_buffer[i] = newUid[i]; - bcc ^= newUid[i]; - } - - // Write BCC byte to buffer - block0_buffer[uidSize] = bcc; - - // Stop encrypted traffic so we can send raw bytes - PCD_StopCrypto1(); - - // Activate UID backdoor - if ( !MIFARE_OpenUidBackdoor(logErrors) ) { - if ( logErrors ) { - Serial.println(F("Activating the UID backdoor failed.")); - } - return false; - } - - // Write modified block 0 back to card - status = MIFARE_Write((byte)0, block0_buffer, (byte)16); - if (status != STATUS_OK) { - if ( logErrors ) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } - - // Wake the card up again - byte atqa_answer[2]; - byte atqa_size = 2; - PICC_WakeupA(atqa_answer, &atqa_size); - - return true; +bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { + + // UID + BCC byte can not be larger than 16 together + if (!newUid || !uidSize || uidSize > 15) { + if (logErrors) { + Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); + } + return false; + } + + // Authenticate for reading + MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + + if (status == STATUS_TIMEOUT) { + // We get a read timeout if no card is selected yet, so let's select one + + // Wake the card up again if sleeping +// byte atqa_answer[2]; +// byte atqa_size = 2; +// PICC_WakeupA(atqa_answer, &atqa_size); + + if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { + Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); + return false; + } + + status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + // We tried, time to give up + if (logErrors) { + Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + else { + if (logErrors) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + + // Read block 0 + byte block0_buffer[18]; + byte byteCount = sizeof(block0_buffer); + status = MIFARE_Read((byte)0, block0_buffer, &byteCount); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); + } + return false; + } + + // Write new UID to the data we just read, and calculate BCC byte + byte bcc = 0; + for (int i = 0; i < uidSize; i++) { + block0_buffer[i] = newUid[i]; + bcc ^= newUid[i]; + } + + // Write BCC byte to buffer + block0_buffer[uidSize] = bcc; + + // Stop encrypted traffic so we can send raw bytes + PCD_StopCrypto1(); + + // Activate UID backdoor + if (!MIFARE_OpenUidBackdoor(logErrors)) { + if (logErrors) { + Serial.println(F("Activating the UID backdoor failed.")); + } + return false; + } + + // Write modified block 0 back to card + status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + + // Wake the card up again + byte atqa_answer[2]; + byte atqa_size = 2; + PICC_WakeupA(atqa_answer, &atqa_size); + + return true; } /** * Resets entire sector 0 to zeroes, so the card can be read again by readers. */ bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { - MIFARE_OpenUidBackdoor( logErrors ); - - byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - // Write modified block 0 back to card - byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); - if (status != STATUS_OK) { - if ( logErrors ) { - Serial.print(F("MIFARE_Write() failed: ")); - Serial.println(GetStatusCodeName(status)); - } - return false; - } + MIFARE_OpenUidBackdoor(logErrors); + + byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Write modified block 0 back to card + byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } } ///////////////////////////////////////////////////////////////////////////////////// diff --git a/MFRC522.h b/MFRC522.h index b0429dd..d2c00da 100644 --- a/MFRC522.h +++ b/MFRC522.h @@ -40,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. @@ -218,8 +218,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. @@ -242,7 +242,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, @@ -305,7 +305,7 @@ public: void PCD_SetRegisterBitMask(byte reg, byte mask); void PCD_ClearRegisterBitMask(byte reg, byte mask); byte PCD_CalculateCRC(byte *data, byte length, byte *result); - + ///////////////////////////////////////////////////////////////////////////////////// // Functions for manipulating the MFRC522 ///////////////////////////////////////////////////////////////////////////////////// @@ -315,17 +315,16 @@ public: void PCD_AntennaOff(); byte PCD_GetAntennaGain(); void PCD_SetAntennaGain(byte mask); - bool PCD_PerformSelfTest(); + 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_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); byte PICC_Select(Uid *uid, byte validBits = 0); byte PICC_HaltA(); @@ -336,14 +335,14 @@ public: 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_Decrement(byte blockAddr, long delta); byte MIFARE_Increment(byte blockAddr, long delta); - byte MIFARE_Restore(byte blockAddr); - byte MIFARE_Transfer(byte blockAddr); + byte MIFARE_Restore(byte blockAddr); + byte MIFARE_Transfer(byte blockAddr); byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); - byte MIFARE_GetValue(byte blockAddr, long *value); - byte MIFARE_SetValue(byte blockAddr, long value); - + byte MIFARE_GetValue(byte blockAddr, long *value); + byte MIFARE_SetValue(byte blockAddr, long value); + ///////////////////////////////////////////////////////////////////////////////////// // Support functions ///////////////////////////////////////////////////////////////////////////////////// @@ -360,16 +359,16 @@ public: 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); + 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) diff --git a/README.rst b/README.rst index a210eae..2813de2 100644 --- a/README.rst +++ b/README.rst @@ -82,13 +82,14 @@ Protocols 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. +* 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. License ------- 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/keywords.txt b/keywords.txt index 5690ce9..73e6216 100644 --- a/keywords.txt +++ b/keywords.txt @@ -19,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 @@ -28,12 +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 @@ -41,6 +49,8 @@ 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 @@ -49,13 +59,22 @@ 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