Pokusy s čtečkou RFID karet MFRC522. Čtečka komunikuje pomocí SPI sběrnice.

Piny

RC522 Pinout

Tabulka 1. Zapojení
MFRC522 pin Pico pin

SDA

GPIO17, SPI0 CSn

SCK

GPIO18, SPI0 SCK

MOSI

GPIO19, SPI0 TX

MISO

GPIO16, SPI0 RX

IRQ

nezapojeno

GND

GND

RST

GPIO20

3.3V

3.3V fyzický pin 36

Zdrojové kódy

pico-mfrc522/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)

include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

project(rfid-rc522)

pico_sdk_init()


add_executable(rfid-rc522
        example.c
	mfrc522.c
        )

target_link_libraries(rfid-rc522 pico_stdlib hardware_spi)

# create map/bin/hex file etc.
pico_add_extra_outputs(rfid-rc522)
pico_enable_stdio_usb(rfid-rc522 1)
pico_enable_stdio_uart(rfid-rc522 0)
pico-mfrc522/example.c
#include "mfrc522.h"

void main() {
    stdio_init_all();

    // Declare card UID's
    uint8_t tag2[] = {0x19, 0x09, 0x29, 0x3e};
    uint8_t tag1[] = {0x5a, 0x23, 0xc8, 0x4f};

    MFRC522Ptr_t mfrc = MFRC522_Init();
    PCD_Init(mfrc, spi0);

    sleep_ms(5000);

    // char test_result = PCD_SelfTest(mfrc);

    // if (test_result == 0) {
    //     printf("Self Test:  SUCCESS");
    // } else {
    //    printf("Self Test: FAILED");
    // }

    while(1) {
        //Wait for new card
        printf("Waiting for card\n\r");
        while(!PICC_IsNewCardPresent(mfrc));
        //Select the card
        printf("Selecting card\n\r");
        PICC_ReadCardSerial(mfrc);

        //Show UID on serial monitor
        printf("PICC dump: \n\r");
        PICC_DumpToSerial(mfrc, &(mfrc->uid));

        //Authorization with uid
        printf("Uid is: ");
        for(int i = 0; i < 4; i++) {
            printf("%x ", mfrc->uid.uidByte[i]);
        } printf("\n\r");

        if(memcmp(mfrc->uid.uidByte, tag1, 4) == 0 || memcmp(mfrc->uid.uidByte, tag2, 4) == 0 ) {
            printf("Authentication Success\n\r");
        } else {
            printf("Authentication Failed\n\r");
        }
    }
}
pico-mfrc522/mfrc522.c
/**
* mfrc522 library for pi pico c/c++ sdk
* NOTE: Please also check the comments in MFRC522.h - they provide useful hints
* and background information.
*/

#include "mfrc522.h"

// ADT object allocation counter
static int MFRC_Instance_Counter = 0;

/**
 * Set up the data structures of an MFRC522 ADT object and return a pointer
 */
MFRC522Ptr_t MFRC522_Init() {
	// allocate instance struct array
	static struct MFRC522_T mfrc_Instances[MFRC_MAX_INSTANCES];
	//      static Chip_SSP_DATA_SETUP_T dataSetup_Instances[MFRC_MAX_INSTANCES];
	//		struct MFRC522_T mfrc_struct;
	//		Chip_SSP_DATA_SETUP_T data_setup;

	// initialize fields
	uint16_t i;
	for (i = 0; i < BUFFER_SIZE; i++) {
		mfrc_Instances[MFRC_Instance_Counter].Rx_Buf[i] = 0;
		mfrc_Instances[MFRC_Instance_Counter].Tx_Buf[i] = 0;
	}

	mfrc_Instances[MFRC_Instance_Counter]._chipSelectPin = cs_pin;

	// update instance counter
	MFRC_Instance_Counter++;

	return &(mfrc_Instances[MFRC_Instance_Counter - 1]);
} 



/*******************************************************************************
* Basic interface functions for communicating with the MFRC522
*******************************************************************************/

/**
 * Writes a uint8_t to the specified register in the MFRC522 chip.
 * The interface is described in the datasheet section 8.1.2.
 */
void PCD_WriteRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t value) {
	uint8_t msg[2];
	msg[0] = 0x00 | reg;
	msg[1] = value;

	cs_select(mfrc->_chipSelectPin);
	spi_write_blocking(mfrc->spi, msg, 2);
	cs_deselect(mfrc->_chipSelectPin);
}

/**
 * Writes a number of uint8_ts to the specified register in the MFRC522 chip.
 * The interface is described in the datasheet section 8.1.2.
 */
void PCD_WriteNRegister(
	MFRC522Ptr_t mfrc,
	uint8_t reg,   ///< The register to write to. One of the PCD_Register enums.
	uint8_t count, ///< The number of uint8_ts to write to the register
	uint8_t *values ///< The values to write. uint8_t array.
	) {
	uint8_t msg[count + 1]; //Values and address
	msg[0] = 0x00 | reg;

	uint8_t i;
	for(i = 0; i < count; i++) {
		msg[i + 1] = values[i];
	}

	cs_select(mfrc->_chipSelectPin);
	spi_write_blocking(mfrc->spi, msg, count + 1);
	cs_deselect(mfrc->_chipSelectPin);
}

/**
 * Reads a uint8_t from the specified register in the MFRC522 chip.
 * The interface is described in the datasheet section 8.1.2.
 */
uint8_t PCD_ReadRegister(
	MFRC522Ptr_t mfrc, 
	uint8_t reg ///< The register to read from. One of the PCD_Register enums
	) {
	uint8_t buf = 0;
	const uint8_t msg = 0x80 | reg;
	
	cs_select(mfrc->_chipSelectPin);
	spi_write_blocking(mfrc->spi, &msg, 1);
	spi_read_blocking(mfrc->spi, 0, &buf, 1);
	cs_deselect(mfrc->_chipSelectPin);
	return buf;
}

/**
 * Reads a number of uint8_ts from the specified register in the MFRC522 chip.
 * The interface is described in the datasheet section 8.1.2.
 */
void PCD_ReadNRegister(
	MFRC522Ptr_t mfrc,
	uint8_t reg, ///< The register to read from. One of the PCD_Register enums.
	uint8_t count,   ///< The number of uint8_ts to read
	uint8_t *values, ///< uint8_t array to store the values in.
	uint8_t rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated.
	) {
	uint8_t buf = 0;
	const uint8_t msg = 0x80 | reg;
	
	cs_select(mfrc->_chipSelectPin);

	// uint8_t i;
	// for(i = 0; i < count; i++) {
	// 	uint8_t value = 0;
	// 	spi_write_blocking(mfrc->spi, &msg, 1);
	// 	spi_read_blocking(mfrc->spi, 0, &value, 1);
	// 	values[i] = value;
	// }

	uint8_t i;
	for(i = 0; i < count; i++) {
		values[i] = PCD_ReadRegister(mfrc, msg);
	}

	cs_deselect(mfrc->_chipSelectPin);
}

/**
 * Sets the bits given in mask in register reg.
 */
void PCD_SetRegisterBitMask(
	MFRC522Ptr_t mfrc,
	uint8_t reg, ///< The register to update. One of the PCD_Register enums.
	uint8_t mask ///< The bits to set.
	) {
	uint8_t tmp;
	tmp = PCD_ReadRegister(mfrc, reg);
	PCD_WriteRegister(mfrc, reg, tmp | mask); // set bit mask
} // End PCD_SetRegisterBitMask()

/**
 * Clears the bits given in mask from register reg.
 */
void PCD_ClearRegisterBitMask(
	MFRC522Ptr_t mfrc,
	uint8_t reg, ///< The register to update. One of the PCD_Register enums.
	uint8_t mask ///< The bits to clear.
	) {
	uint8_t tmp;
	tmp = PCD_ReadRegister(mfrc, reg);
	PCD_WriteRegister(mfrc, reg, tmp & (~mask)); // clear bit mask
} // End PCD_ClearRegisterBitMask()

/**
 * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode
PCD_CalculateCRC(MFRC522Ptr_t mfrc,
				 uint8_t *data,  ///< In: Pointer to the data to transfer to the
								 ///FIFO for CRC calculation.
				 uint8_t length, ///< In: The number of uint8_ts to transfer.
				 uint8_t *result ///< Out: Pointer to result buffer. Result is
								 ///written to result[0..1], low uint8_t first.
				 ) {
	PCD_WriteRegister(mfrc, CommandReg, PCD_Idle); // Stop any active command.
	PCD_WriteRegister(mfrc, DivIrqReg,
					  0x04); // Clear the CRCIRq interrupt request bit
	PCD_SetRegisterBitMask(mfrc, FIFOLevelReg,
						   0x80); // FlushBuffer = 1, FIFO initialization
	PCD_WriteNRegister(mfrc, FIFODataReg, length,
					   data);						  // Write data to the FIFO
	PCD_WriteRegister(mfrc, CommandReg, PCD_CalcCRC); // Start the calculation

	// Wait for the CRC calculation to complete. Each iteration of the
	// while-loop takes 17.73�s.
	uint16_t i = 5000;
	uint8_t n;
	while (1) {
		n = PCD_ReadRegister(mfrc, DivIrqReg); // DivIrqReg[7..0] bits are: Set2
											   // reserved reserved MfinActIRq
											   // reserved CRCIRq reserved
											   // reserved
		if (n & 0x04) { // CRCIRq bit set - calculation done
			break;
		}
		if (--i == 0) { // The emergency break. We will eventually terminate on
						// this one after 89ms. Communication with the MFRC522
						// might be down.
			return STATUS_TIMEOUT;
		}
	}
	PCD_WriteRegister(
		mfrc, 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(mfrc, CRCResultRegL);
	result[1] = PCD_ReadRegister(mfrc, CRCResultRegH);
	return STATUS_OK;
} // End PCD_CalculateCRC()

/*******************************************************************************
* Functions for manipulating the MFRC522
*******************************************************************************/

/**
 * Initializes the MFRC522 chip.
 */
void PCD_Init(MFRC522Ptr_t mfrc, spi_inst_t *spi) {

	mfrc->spi = spi0;
	gpio_put(RESET_PIN, 0);
    sleep_ms(1000);
    gpio_put(RESET_PIN, 1);
	sleep_ms(50);

    gpio_init(cs_pin);
    gpio_set_dir(cs_pin, GPIO_OUT);
    gpio_put(cs_pin, 1);

    spi_init(spi0, 1000000);

    spi_set_format(spi0, 8, 0, 0, SPI_MSB_FIRST);

    gpio_set_function(sck_pin, GPIO_FUNC_SPI);
    gpio_set_function(mosi_pin, GPIO_FUNC_SPI);
    gpio_set_function(miso_pin, GPIO_FUNC_SPI);

	PCD_WriteRegister(mfrc, CommandReg, PCD_SoftReset);

	// 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(mfrc, TModeReg, 0x80); // TAuto=1; timer starts
											 // automatically at the end of the
											 // transmission in all
											 // communication modes at all
											 // speeds
	PCD_WriteRegister(mfrc, TPrescalerReg,
					  0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie
							 // 0x0A9 = 169 => f_timer=40kHz, ie a timer period
							 // of 25�s.
	PCD_WriteRegister(
		mfrc, TReloadRegH,
		0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout.
	PCD_WriteRegister(mfrc, TReloadRegL, 0xE8);

	PCD_WriteRegister(mfrc, TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK
											 // modulation independent of the
											 // ModGsPReg register setting
	PCD_WriteRegister(mfrc, 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(mfrc); // Enable the antenna driver pins TX1 and TX2 (they
						 // were disabled by the reset)
} // End PCD_Init()

/**
 * Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
 */
void PCD_Reset(MFRC522Ptr_t mfrc) {
	PCD_WriteRegister(mfrc, CommandReg,
					  PCD_SoftReset); // Issue the SoftReset command.
	// The datasheet does not mention how long the SoftRest command takes to
	// complete.
	// But the MFRC522 might have been in soft power-down mode (triggered by bit
	// 4 of CommandReg)
	// Section 8.8.2 in the datasheet says the oscillator start-up time is the
	// start up time of the crystal + 37,74�s. Let us be generous: 50ms.
	//SysTick_Init();
	sleep_ms(50);
	// Wait for the PowerDown bit in CommandReg to be cleared
	while (PCD_ReadRegister(mfrc, CommandReg) & (1 << 4)) {
		// PCD still restarting - unlikely after waiting 50ms, but better safe
		// than sorry.
	}
} // End PCD_Reset()

/**
 * Turns the antenna on by enabling pins TX1 and TX2.
 * After a reset these pins are disabled.
 */
void PCD_AntennaOn(MFRC522Ptr_t mfrc) {
	uint8_t value = PCD_ReadRegister(mfrc, TxControlReg);
	if ((value & 0x03) != 0x03) {
		PCD_WriteRegister(mfrc, TxControlReg, value | 0x03);
	}
} // End PCD_AntennaOn()

/**
 * Turns the antenna off by disabling pins TX1 and TX2.
 */
void PCD_AntennaOff(MFRC522Ptr_t mfrc) {
	PCD_ClearRegisterBitMask(mfrc, 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.
 */
uint8_t PCD_GetAntennaGain(MFRC522Ptr_t mfrc) {
	return PCD_ReadRegister(mfrc, 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 PCD_SetAntennaGain(MFRC522Ptr_t mfrc, uint8_t mask) {
	if (PCD_GetAntennaGain(mfrc) != mask) { // only bother if there is a change
		PCD_ClearRegisterBitMask(
			mfrc, RFCfgReg, (0x07 << 4)); // clear needed to allow 000 pattern
		PCD_SetRegisterBitMask(mfrc, RFCfgReg,
							   mask & (0x07 << 4)); // only set RxGain[2:0] bits
	}
} // End PCD_SetAntennaGain()

/**
 * Performs the MFRC522 self test described in 16.1.1
 * Returns 0 if test is ok. Check what version of MFRC522 and put the buffer values in SELF_TEST_BYTES[]
*/
uint8_t PCD_SelfTest(MFRC522Ptr_t mfrc) {

    //Perform a soft reset
    PCD_WriteRegister(mfrc, CommandReg, PCD_SoftReset);
    sleep_ms(50); //Allow the chip to reset
    //printf("Soft reset complete\n\r");

    //Clear the internal buffer by writing 25 bytes of 00h (and implement the config command)??.
    PCD_WriteRegister(mfrc, FIFOLevelReg, 0x80); //FIFOLevel indicates nbr of bytes in FIFO buffer. MSB = 1 -> Flush

    for (int i = 0; i < 25; i++) {
        PCD_WriteRegister(mfrc, FIFODataReg, 0x00);
    }
    PCD_WriteRegister(mfrc, CommandReg, PCD_Mem); //Transfer the 25 bytes from FIFO to internal buffer
    //printf("Clearing of internal buffer complete\n\r");

    //Enable the self test by writing 09h to the AutoTestReg register.
    PCD_WriteRegister(mfrc, AutoTestReg, PCD_EnableSelfTest);
    //printf("Self test enable complete\n\r");

    //Write 00h to the FIFO buffer.
    PCD_WriteRegister(mfrc, FIFODataReg, 0x00);
    //printf("Written 0x00 to FIFO buffer\n\r");

    //Start the self test with the CalcCRC command.
    PCD_WriteRegister(mfrc, CommandReg, PCD_CalcCRC);
    //printf("Started self test with the CRC command\n\r");

    //The self test is initiated. Wait for completion
    bool self_test_complete = false;
    while (!self_test_complete) {
        uint8_t buf = PCD_ReadRegister(mfrc, FIFOLevelReg);
        //printf("%x\n\r", buf);
        if (buf >= 64) {
            self_test_complete = true;
        }
    }
    //printf("Self test completed\n\r");

    //Stop calculating CRC with idle command???

    //When the self test has completed, the FIFO buffer contains the version bytes.
    uint8_t result[64];
    for (int i = 0; i < 64; i++) {
        uint8_t buf = PCD_ReadRegister(mfrc, FIFODataReg);
        result[i] = buf;
    }
    //printf("Bytes copied to result array\n\r");

    //Check if the received bytes correspond to the versions.
    PCD_WriteRegister(mfrc, AutoTestReg, 0x00); //Disable self-test
    //printf("Disabled self test\n\r");

    /*printf("Self test result:\n\r");
    for (uint8_t i = 0; i < 64; i++) {
        printf("%x, ", result[i]);
        if (i % 10 == 0) printf("\n\r");
    } printf("\n\r"); */

    for (uint8_t i = 0; i < 64; i++) {
		if (result[i] != SELF_TEST_BYTES[i]) {
			return -1;
		}
	}

    return 0;

	}


/*******************************************************************************
* Functions for communicating with PICCs
*******************************************************************************/
/**
 * Executes the Transceive command.
 * CRC validation can only be done if backData and backLen are specified.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PCD_TransceiveData(
	MFRC522Ptr_t mfrc,
	uint8_t *sendData,  ///< Pointer to the data to transfer to the FIFO.
	uint8_t sendLen,	///< Number of uint8_ts to transfer to the FIFO.
	uint8_t *backData,  ///< NULL or pointer to buffer if data should be read
						///back after executing the command.
	uint8_t *backLen,   ///< In: Max number of uint8_ts to write to *backData.
						///Out: The number of uint8_ts returned.
	uint8_t *validBits, ///< In/Out: The number of valid bits in the last
						///uint8_t. 0 for 8 valid bits. Default NULL.
	uint8_t rxAlign,	///< In: Defines the bit position in backData[0] for the
						///first bit received. Default 0.
	bool checkCRC ///< In: True => The last two uint8_ts of the response is
				  ///assumed to be a CRC_A that must be validated.
	) {
	//		 validBits=NULL;
	//		 rxAlign=0;
	//		 checkCRC=false;

	uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
	return PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, sendData,
								   sendLen, backData, backLen, validBits,
								   rxAlign, checkCRC);
} // End PCD_TransceiveData()

/**
 * Transfers data to the MFRC522 FIFO, executes a command, waits for completion
 *and transfers data back from the FIFO.
 * CRC validation can only be done if backData and backLen are specified.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PCD_CommunicateWithPICC(
	MFRC522Ptr_t mfrc,
	uint8_t command, ///< The command to execute. One of the PCD_Command enums.
	uint8_t waitIRq, ///< The bits in the ComIrqReg register that signals
					 ///successful completion of the command.
	uint8_t *sendData,  ///< Pointer to the data to transfer to the FIFO.
	uint8_t sendLen,	///< Number of uint8_ts to transfer to the FIFO.
	uint8_t *backData,  ///< NULL or pointer to buffer if data should be read
						///back after executing the command.
	uint8_t *backLen,   ///< In: Max number of uint8_ts to write to *backData.
						///Out: The number of uint8_ts returned.
	uint8_t *validBits, ///< In/Out: The number of valid bits in the last
						///uint8_t. 0 for 8 valid bits.
	uint8_t rxAlign,	///< In: Defines the bit position in backData[0] for the
						///first bit received. Default 0.
	bool checkCRC ///< In: True => The last two uint8_ts of the response is
				  ///assumed to be a CRC_A that must be validated.
	) {
	//		backData=NULL;
	//		backLen=NULL;
	//		validBits=NULL;
	//		rxAlign=0;
	//		checkCRC=false;

	uint8_t n, _validBits;
	unsigned int i;

	// Prepare values for BitFramingReg
	uint8_t txLastBits = validBits ? *validBits : 0;
	uint8_t bitFraming =
		(rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4].
									 // TxLastBits = BitFramingReg[2..0]

	PCD_WriteRegister(mfrc, CommandReg, PCD_Idle); // Stop any active command.
	PCD_WriteRegister(mfrc, ComIrqReg,
					  0x7F); // Clear all seven interrupt request bits
	PCD_SetRegisterBitMask(mfrc, FIFOLevelReg,
						   0x80); // FlushBuffer = 1, FIFO initialization
	PCD_WriteNRegister(mfrc, FIFODataReg, sendLen,
					   sendData); // Write sendData to the FIFO
	PCD_WriteRegister(mfrc, BitFramingReg, bitFraming); // Bit adjustments
	PCD_WriteRegister(mfrc, CommandReg, command);		// Execute the command
	if (command == PCD_Transceive) {
		PCD_SetRegisterBitMask(
			mfrc, BitFramingReg,
			0x80); // StartSend=1, transmission of data starts
	}

	// Wait for the command to complete.
	// In PCD_Init() we set the TAuto flag in TModeReg. This means the timer
	// automatically starts when the PCD stops transmitting.
	// Each iteration of the do-while-loop takes 17.86�s.
	i = 2000;
	while (1) {
		n = PCD_ReadRegister(mfrc, ComIrqReg); // ComIrqReg[7..0] bits are: Set1
											   // TxIRq RxIRq IdleIRq HiAlertIRq
											   // LoAlertIRq ErrIRq TimerIRq
		if (n & waitIRq) { // One of the interrupts that signal success has been
						   // set.
			break;
		}
		if (n & 0x01) { // Timer interrupt - nothing received in 25ms
			return STATUS_TIMEOUT;
		}
		if (--i == 0) { // The emergency break. If all other conditions fail we
						// will eventually terminate on this one after 35.7ms.
						// Communication with the MFRC522 might be down.
			return STATUS_TIMEOUT;
		}
	}

	// Stop now if any errors except collisions were detected.
	uint8_t errorRegValue =
		PCD_ReadRegister(mfrc, 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(mfrc,
							 FIFOLevelReg); // Number of uint8_ts in the FIFO
		if (n > *backLen) {
			return STATUS_NO_ROOM;
		}
		*backLen = n; // Number of uint8_ts returned
		PCD_ReadNRegister(mfrc, FIFODataReg, n, backData,
						  rxAlign); // Get received data from FIFO
		_validBits = PCD_ReadRegister(mfrc, ControlReg) &
					 0x07; // RxLastBits[2:0] indicates the number of valid bits
						   // in the last received uint8_t. If this value is
						   // 000b, the whole uint8_t is valid.
		if (validBits) {
			*validBits = _validBits;
		}
	}

	// Tell about collisions
	if (errorRegValue & 0x08) { // CollErr
		return STATUS_COLLISION;
	}

	// Perform CRC_A validation if requested.
	if (backData && backLen && checkCRC) {
		// In this case a MIFARE Classic NAK is not OK.
		if (*backLen == 1 && _validBits == 4) {
			return STATUS_MIFARE_NACK;
		}
		// We need at least the CRC_A value and all 8 bits of the last uint8_t
		// must be received.
		if (*backLen < 2 || _validBits != 0) {
			return STATUS_CRC_WRONG;
		}
		// Verify CRC_A - do our own calculation and store the control in
		// controlBuffer.
		uint8_t controlBuffer[2];
		StatusCode status = PCD_CalculateCRC(mfrc, &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;
		}
	}

	return STATUS_OK;
} // End PCD_CommunicateWithPICC()

/**
 * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to
 *READY and prepare for anticollision or selection. 7 bit frame.
 * Beware: When two PICCs are in the field at the same time I often get
 *STATUS_TIMEOUT - probably due do bad antenna design.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PICC_RequestA(
	MFRC522Ptr_t mfrc,
	uint8_t *
		bufferATQA,		///< The buffer to store the ATQA (Answer to request) in
	uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
						///uint8_ts returned if STATUS_OK.
	) {
	return PICC_REQA_or_WUPA(mfrc, PICC_CMD_REQA, bufferATQA, bufferSize);
} // End PICC_RequestA()

/**
 * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to
 *go to READY(*) and prepare for anticollision or selection. 7 bit frame.
 * Beware: When two PICCs are in the field at the same time I often get
 *STATUS_TIMEOUT - probably due do bad antenna design.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PICC_WakeupA(
	MFRC522Ptr_t mfrc,
	uint8_t *
		bufferATQA,		///< The buffer to store the ATQA (Answer to request) in
	uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
						///uint8_ts returned if STATUS_OK.
	) {
	return PICC_REQA_or_WUPA(mfrc, PICC_CMD_WUPA, bufferATQA, bufferSize);
} // End PICC_WakeupA()

/**
 * Transmits REQA or WUPA commands.
 * Beware: When two PICCs are in the field at the same time I often get
 *STATUS_TIMEOUT - probably due do bad antenna design.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PICC_REQA_or_WUPA(
	MFRC522Ptr_t mfrc,
	uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
	uint8_t *
		bufferATQA,		///< The buffer to store the ATQA (Answer to request) in
	uint8_t *bufferSize ///< Buffer size, at least two uint8_ts. Also number of
						///uint8_ts returned if STATUS_OK.
	) {
	uint8_t validBits;
	StatusCode status;

	if (bufferATQA == NULL ||
		*bufferSize < 2) { // The ATQA response is 2 uint8_ts long.
		return STATUS_NO_ROOM;
	}
	PCD_ClearRegisterBitMask(mfrc, 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) uint8_t.
				   // TxLastBits = BitFramingReg[2..0]
	status = PCD_TransceiveData(mfrc, &command, 1, bufferATQA, bufferSize,
								&validBits, 0, false);
	if (status != STATUS_OK) {
		return status;
	}
	if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits.
		return STATUS_ERROR;
	}
	return STATUS_OK;
} // End PICC_REQA_or_WUPA()

/**
 * Transmits SELECT/ANTICOLLISION commands to select a single PICC.
 * Before calling this function the PICCs must be placed in the READY(*) state
 *by calling PICC_RequestA() or PICC_WakeupA().
 * On success:
 *		- The chosen PICC is in state ACTIVE(*) and all other PICCs have returned
 *to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.)
 *		- The UID size and value of the chosen PICC is returned in *uid along with
 *the SAK.
 *
 * A PICC UID consists of 4, 7 or 10 uint8_ts.
 * Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs
 *two or three iterations are used:
 *		UID size	Number of UID uint8_ts		Cascade levels		Example of
 *PICC
 *		========	===================		==============		===============
 *		single				 4						1				MIFARE
 *Classic
 *		double				 7						2				MIFARE
 *Ultralight
 *		triple				10						3				Not currently in
 *use?
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PICC_Select(MFRC522Ptr_t mfrc,
					   Uid *uid, ///< Pointer to Uid struct. Normally output,
								 ///but can also be used to supply a known UID.
					   uint8_t validBits ///< The number of known UID bits
										 ///supplied in *uid. Normally 0. If set
										 ///you must also supply uid->size.
					   ) {
	//		validBits=0;

	bool uidComplete;
	bool selectDone;
	bool useCascadeTag;
	uint8_t cascadeLevel = 1;
	StatusCode result;
	uint8_t count;
	uint8_t index;
	uint8_t uidIndex; // The first index in uid->uiduint8_t[] that is used in
					  // the current Cascade Level.
	int8_t currentLevelKnownBits; // The number of known UID bits in the current
								  // Cascade Level.
	uint8_t buffer[9];  // The SELECT/ANTICOLLISION commands uses a 7 uint8_t
						// standard frame + 2 uint8_ts CRC_A
	uint8_t bufferUsed; // The number of uint8_ts used in the buffer, ie the
						// number of uint8_ts to transfer to the FIFO.
	uint8_t rxAlign; // Used in BitFramingReg. Defines the bit position for the
					 // first bit received.
	uint8_t txLastBits; // Used in BitFramingReg. The number of valid bits in
						// the last transmitted uint8_t.
	uint8_t *responseBuffer;
	uint8_t responseLength;

	// Description of buffer structure:
	//		uint8_t 0: SEL				Indicates the Cascade Level: PICC_CMD_SEL_CL1,
	//PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
	//		uint8_t 1: NVB					Number of Valid Bits (in complete command, not
	//just the UID): High nibble: complete uint8_ts, Low nibble: Extra bits.
	//		uint8_t 2: UID-data or CT		See explanation below. CT means Cascade
	//Tag.
	//		uint8_t 3: UID-data
	//		uint8_t 4: UID-data
	//		uint8_t 5: UID-data
	//		uint8_t 6: BCC					Block Check Character - XOR of uint8_ts
	//2-5
	//		uint8_t 7: CRC_A
	//		uint8_t 8: CRC_A
	// The BCC and CRC_A are only transmitted if we know all the UID bits of the
	// current Cascade Level.
	//
	// Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft:
	// UID contents and cascade levels)
	//		UID size	Cascade level	uint8_t2	uint8_t3	uint8_t4
	//uint8_t5
	//		========	=============	=====	=====	=====	=====
	//		 4 uint8_ts		1			uid0	uid1	uid2	uid3
	//		 7 uint8_ts		1			CT		uid0	uid1	uid2
	//						2			uid3	uid4	uid5	uid6
	//		10 uint8_ts		1			CT		uid0	uid1	uid2
	//						2			CT		uid3	uid4	uid5
	//						3			uid6	uid7	uid8	uid9

	// Sanity checks
	if (validBits > 80) {
		return STATUS_INVALID;
	}

	// Prepare MFRC522
	PCD_ClearRegisterBitMask(mfrc, CollReg, 0x80); // ValuesAfterColl=1 => Bits
												   // received after collision
												   // are cleared.

	// Repeat Cascade Level loop until we have a complete UID.
	uidComplete = false;
	while (!uidComplete) {
		// Set the Cascade Level in the SEL uint8_t, find out if we need to use
		// the Cascade Tag in uint8_t 2.
		switch (cascadeLevel) {
		case 1:
			buffer[0] = PICC_CMD_SEL_CL1;
			uidIndex = 0;
			useCascadeTag =
				validBits &&
				uid->size >
					4; // When we know that the UID has more than 4 uint8_ts
			break;

		case 2:
			buffer[0] = PICC_CMD_SEL_CL2;
			uidIndex = 3;
			useCascadeTag =
				validBits &&
				uid->size >
					7; // When we know that the UID has more than 7 uint8_ts
			break;

		case 3:
			buffer[0] = PICC_CMD_SEL_CL3;
			uidIndex = 6;
			useCascadeTag = false; // Never used in CL3.
			break;

		default:
			return STATUS_INTERNAL_ERROR;
			break;
		}

		// How many UID bits are known in this Cascade Level?
		currentLevelKnownBits = validBits - (8 * uidIndex);
		if (currentLevelKnownBits < 0) {
			currentLevelKnownBits = 0;
		}
		// Copy the known bits from uid->uiduint8_t[] to buffer[]
		index = 2; // destination index in buffer[]
		if (useCascadeTag) {
			buffer[index++] = PICC_CMD_CT;
		}
		uint8_t uint8_tsToCopy =
			currentLevelKnownBits / 8 +
			(currentLevelKnownBits % 8 ? 1 : 0); // The number of uint8_ts
												 // needed to represent the
												 // known bits for this level.
		if (uint8_tsToCopy) {
			uint8_t maxuint8_ts =
				useCascadeTag ? 3 : 4; // Max 4 uint8_ts in each Cascade Level.
									   // Only 3 left if we use the Cascade Tag
			if (uint8_tsToCopy > maxuint8_ts) {
				uint8_tsToCopy = maxuint8_ts;
			}
			for (count = 0; count < uint8_tsToCopy; count++) {
				buffer[index++] = uid->uidByte[uidIndex + count];
			}
		}
		// Now that the data has been copied we need to include the 8 bits in CT
		// in currentLevelKnownBits
		if (useCascadeTag) {
			currentLevelKnownBits += 8;
		}

		// Repeat anti collision loop until we can transmit all UID bits + BCC
		// and receive a SAK - max 32 iterations.
		selectDone = false;
		while (!selectDone) {
			// Find out how many bits and uint8_ts to send and receive.
			if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade
											   // Level are known. This is a
											   // SELECT.
				// printf(F("SELECT: currentLevelKnownBits="));
				// printf(currentLevelKnownBits, DEC);
				buffer[1] =
					0x70; // NVB - Number of Valid Bits: Seven whole uint8_ts
				// Calculate BCC - Block Check Character
				buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
				// Calculate CRC_A
				result = PCD_CalculateCRC(mfrc, buffer, 7, &buffer[7]);
				if (result != STATUS_OK) {
					return result;
				}
				txLastBits = 0; // 0 => All 8 bits are valid.
				bufferUsed = 9;
				// Store response in the last 3 uint8_ts of buffer (BCC and
				// CRC_A - not needed after tx)
				responseBuffer = &buffer[6];
				responseLength = 3;
			} else { // This is an ANTICOLLISION.
				// printf(F("ANTICOLLISION: currentLevelKnownBits="));
				// printf(currentLevelKnownBits, DEC);
				txLastBits = currentLevelKnownBits % 8;
				count = currentLevelKnownBits /
						8;		   // Number of whole uint8_ts in the UID part.
				index = 2 + count; // Number of whole uint8_ts: SEL + NVB + UIDs
				buffer[1] =
					(index << 4) + txLastBits; // NVB - Number of Valid Bits
				bufferUsed = index + (txLastBits ? 1 : 0);
				// Store response in the unused part of buffer
				responseBuffer = &buffer[index];
				responseLength = sizeof(buffer) - index;
			}

			// Set bit adjustments
			rxAlign = txLastBits; // Having a separate variable is overkill. But
								  // it makes the next line easier to read.
			PCD_WriteRegister(mfrc, BitFramingReg,
							  (rxAlign << 4) +
								  txLastBits); // RxAlign = BitFramingReg[6..4].
											   // TxLastBits =
											   // BitFramingReg[2..0]

			// Transmit the buffer and receive the response.
			result = PCD_TransceiveData(mfrc, buffer, bufferUsed,
										responseBuffer, &responseLength,
										&txLastBits, rxAlign, false);
			if (result == STATUS_COLLISION) { // More than one PICC in the field
											  // => collision.
				uint8_t valueOfCollReg = PCD_ReadRegister(
					mfrc, 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
				}
				uint8_t collisionPos =
					valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32.
				if (collisionPos == 0) {
					collisionPos = 32;
				}
				if (collisionPos <=
					currentLevelKnownBits) { // No progress - should not happen
					return STATUS_INTERNAL_ERROR;
				}
				// Choose the PICC with the bit set.
				currentLevelKnownBits = collisionPos;
				count = (currentLevelKnownBits - 1) % 8; // The bit to modify
				index = 1 + (currentLevelKnownBits / 8) +
						(count ? 1 : 0); // First uint8_t is index 0.
				buffer[index] |= (1 << count);
			} else if (result != STATUS_OK) {
				return result;
			} else {							   // STATUS_OK
				if (currentLevelKnownBits >= 32) { // This was a SELECT.
					selectDone = true;			   // No more anticollision
					// We continue below outside the while.
				} else { // This was an ANTICOLLISION.
					// We now have all 32 bits of the UID in this Cascade Level
					currentLevelKnownBits = 32;
					// Run loop again to do the SELECT.
				}
			}
		} // End of while (!selectDone)

		// We do not check the CBB - it was constructed by us above.

		// Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[]
		index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[]
		uint8_tsToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
		for (count = 0; count < uint8_tsToCopy; count++) {
			uid->uidByte[uidIndex + count] = buffer[index++];
		}

		// Check response SAK (Select Acknowledge)
		if (responseLength != 3 ||
			txLastBits !=
				0) { // SAK must be exactly 24 bits (1 uint8_t + CRC_A).
			return STATUS_ERROR;
		}
		// Verify CRC_A - do our own calculation and store the control in
		// buffer[2..3] - those uint8_ts are not needed anymore.
		result = PCD_CalculateCRC(mfrc, responseBuffer, 1, &buffer[2]);
		if (result != STATUS_OK) {
			return result;
		}
		if ((buffer[2] != responseBuffer[1]) ||
			(buffer[3] != responseBuffer[2])) {
			return STATUS_CRC_WRONG;
		}
		if (responseBuffer[0] &
			0x04) { // Cascade bit set - UID not complete yes
			cascadeLevel++;
		} else {
			uidComplete = true;
			uid->sak = responseBuffer[0];
		}
	} // End of while (!uidComplete)

	// Set correct uid->size
	uid->size = 3 * cascadeLevel + 1;

	return STATUS_OK;
} // End PICC_Select()

/**
 * Instructs a PICC in state ACTIVE(*) to go to state HALT.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PICC_HaltA(MFRC522Ptr_t mfrc) {
	StatusCode result;
	uint8_t buffer[4];

	// Build command buffer
	buffer[0] = PICC_CMD_HLTA;
	buffer[1] = 0;
	// Calculate CRC_A
	result = PCD_CalculateCRC(mfrc, buffer, 2, &buffer[2]);
	if (result != STATUS_OK) {
		return result;
	}

	// Send the command.
	// The standard says:
	//		If the PICC responds with any modulation during a period of 1 ms after
	//the end of the frame containing the
	//		HLTA command, this response shall be interpreted as 'not
	//acknowledge'.
	// We interpret that this way: Only STATUS_TIMEOUT is a success.
	result = PCD_TransceiveData(mfrc, buffer, sizeof(buffer), NULL, 0, NULL, 0,
								false);
	if (result == STATUS_TIMEOUT) {
		return STATUS_OK;
	}
	if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-)
		return STATUS_ERROR;
	}
	return result;
} // End PICC_HaltA()

/*******************************************************************************
*Functions for communicating with MIFARE PICCs
*******************************************************************************/

/**
 * Executes the MFRC522 MFAuthent command.
 * This command manages MIFARE authentication to enable a secure communication
 *to any MIFARE Mini, MIFARE 1K and MIFARE 4K card.
 * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and
 *http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1.
 * For use with MIFARE Classic PICCs.
 * The PICC must be selected - ie in state ACTIVE(*) - before calling this
 *function.
 * Remember to call PCD_StopCrypto1() after communicating with the authenticated
 *PICC - otherwise no new communications can start.
 *
 * All keys are set to FFFFFFFFFFFFh at chip delivery.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT
 *if you supply the wrong key.
 */
StatusCode PCD_Authenticate(
	MFRC522Ptr_t mfrc,
	uint8_t command,   ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B
	uint8_t blockAddr, ///< The block number. See numbering in the comments in
					   ///the .h file.
	MIFARE_Key *key,   ///< Pointer to the Crypteo1 key to use (6 uint8_ts)
	Uid *
		uid ///< Pointer to Uid struct. The first 4 uint8_ts of the UID is used.
	) {
	uint8_t waitIRq = 0x10; // IdleIRq

	// Build command buffer
	uint8_t sendData[12];
	sendData[0] = command;
	sendData[1] = blockAddr;
	for (uint8_t i = 0; i < MF_KEY_SIZE; i++) { // 6 key uint8_ts
		sendData[2 + i] = key->keybyte[i];
	}
	for (uint8_t i = 0; i < 4; i++) { // The first 4 uint8_ts of the UID
		sendData[8 + i] = uid->uidByte[i];
	}

	// Start the authentication.
	return PCD_CommunicateWithPICC(mfrc, PCD_MFAuthent, waitIRq, &sendData[0],
								   sizeof(sendData), NULL, 0, 0, 0, false);
} // End PCD_Authenticate()

/**
 * Used to exit the PCD from its authenticated state.
 * Remember to call this function after communicating with an authenticated PICC
 * - otherwise no new communications can start.
 */
void PCD_StopCrypto1(MFRC522Ptr_t mfrc) {
	// Clear MFCrypto1On bit
	PCD_ClearRegisterBitMask(mfrc, Status2Reg, 0x08); // Status2Reg[7..0] bits
													  // are: TempSensClear
													  // I2CForceHS reserved
													  // reserved MFCrypto1On
													  // ModemState[2:0]
} // End PCD_StopCrypto1()

/**
 * Reads 16 uint8_ts (+ 2 uint8_ts CRC_A) from the active PICC.
 *
 * For MIFARE Classic the sector containing the block must be authenticated
 *before calling this function.
 *
 * For MIFARE Ultralight only addresses 00h to 0Fh are decoded.
 * The MF0ICU1 returns a NAK for higher addresses.
 * The MF0ICU1 responds to the READ command by sending 16 uint8_ts starting from
 *the page address defined by the command argument.
 * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned.
 * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages
 *0Eh, 0Fh, 00h and 01h are returned.
 *
 * The buffer must be at least 18 uint8_ts because a CRC_A is also returned.
 * Checks the CRC_A before returning STATUS_OK.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Read(MFRC522Ptr_t mfrc,
					   uint8_t blockAddr, ///< MIFARE Classic: The block
										  ///(0-0xff) number. MIFARE Ultralight:
										  ///The first page to return data from.
					   uint8_t *buffer,   ///< The buffer to store the data in
					   uint8_t *bufferSize ///< Buffer size, at least 18
										   ///uint8_ts. Also number of uint8_ts
										   ///returned if STATUS_OK.
					   ) {
	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;
	// Calculate CRC_A
	result = PCD_CalculateCRC(mfrc, buffer, 2, &buffer[2]);
	if (result != STATUS_OK) {
		return result;
	}

	// Transmit the buffer and receive the response, validate CRC_A.
	return PCD_TransceiveData(mfrc, buffer, 4, buffer, bufferSize, NULL, 0,
							  true);
} // End MIFARE_Read()

/**
 * Writes 16 uint8_ts to the active PICC.
 *
 * For MIFARE Classic the sector containing the block must be authenticated
 *before calling this function.
 *
 * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE".
 * Even though 16 uint8_ts are transferred to the Ultralight PICC, only the
 *least significant 4 uint8_ts (uint8_ts 0 to 3)
 * are written to the specified address. It is recommended to set the remaining
 *uint8_ts 04h to 0Fh to all logic 0.
 * *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode
MIFARE_Write(MFRC522Ptr_t mfrc,
			 uint8_t blockAddr, ///< MIFARE Classic: The block (0-0xff) number.
								///MIFARE Ultralight: The page (2-15) to write
								///to.
			 uint8_t *buffer,   ///< The 16 uint8_ts to write to the PICC
			 uint8_t bufferSize ///< Buffer size, must be at least 16 uint8_ts.
								///Exactly 16 uint8_ts are written.
			 ) {
	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.
	uint8_t cmdBuffer[2];
	cmdBuffer[0] = PICC_CMD_MF_WRITE;
	cmdBuffer[1] = blockAddr;
	result = PCD_MIFARE_Transceive(
		mfrc, cmdBuffer, 2,
		false); // Adds CRC_A and checks that the response is MF_ACK.
	if (result != STATUS_OK) {
		return result;
	}

	// Step 2: Transfer the data
	result = PCD_MIFARE_Transceive(
		mfrc, buffer, bufferSize,
		false); // Adds CRC_A and checks that the response is MF_ACK.
	if (result != STATUS_OK) {
		return result;
	}

	return STATUS_OK;
} // End MIFARE_Write()

/**
 * Writes a 4 uint8_t page to the active MIFARE Ultralight PICC.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Ultralight_Write(
	MFRC522Ptr_t mfrc,
	uint8_t page,	  ///< The page (2-15) to write to.
	uint8_t *buffer,   ///< The 4 uint8_ts to write to the PICC
	uint8_t bufferSize ///< Buffer size, must be at least 4 uint8_ts. Exactly 4
					   ///uint8_ts are written.
	) {
	StatusCode result;

	// Sanity check
	if (buffer == NULL || bufferSize < 4) {
		return STATUS_INVALID;
	}

	// Build commmand buffer
	uint8_t cmdBuffer[6];
	cmdBuffer[0] = PICC_CMD_UL_WRITE;
	cmdBuffer[1] = page;
	memcpy(&cmdBuffer[2], buffer, 4);

	// Perform the write
	result = PCD_MIFARE_Transceive(
		mfrc, cmdBuffer, 6,
		false); // Adds CRC_A and checks that the response is MF_ACK.
	if (result != STATUS_OK) {
		return result;
	}
	return STATUS_OK;
} // End MIFARE_Ultralight_Write()

/**
 * MIFARE Decrement subtracts the delta from the value of the addressed block,
 *and stores the result in a volatile memory.
 * For MIFARE Classic only. The sector containing the block must be
 *authenticated before calling this function.
 * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
 *or [001].
 * Use MIFARE_Transfer() to store the result in a block.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Decrement(
	MFRC522Ptr_t mfrc,
	uint8_t blockAddr, ///< The block (0-0xff) number.
	long delta ///< This number is subtracted from the value of block blockAddr.
	) {
	return MIFARE_TwoStepHelper(mfrc, PICC_CMD_MF_DECREMENT, blockAddr, delta);
} // End MIFARE_Decrement()

/**
 * MIFARE Increment adds the delta to the value of the addressed block, and
 *stores the result in a volatile memory.
 * For MIFARE Classic only. The sector containing the block must be
 *authenticated before calling this function.
 * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
 *or [001].
 * Use MIFARE_Transfer() to store the result in a block.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Increment(
	MFRC522Ptr_t mfrc,
	uint8_t blockAddr, ///< The block (0-0xff) number.
	long delta ///< This number is added to the value of block blockAddr.
	) {
	return MIFARE_TwoStepHelper(mfrc, PICC_CMD_MF_INCREMENT, blockAddr, delta);
} // End MIFARE_Increment()

/**
 * MIFARE Restore copies the value of the addressed block into a volatile
 *memory.
 * For MIFARE Classic only. The sector containing the block must be
 *authenticated before calling this function.
 * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
 *or [001].
 * Use MIFARE_Transfer() to store the result in a block.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Restore(MFRC522Ptr_t mfrc,
						  uint8_t 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(mfrc, PICC_CMD_MF_RESTORE, blockAddr, 0L);
} // End MIFARE_Restore()

/**
 * Helper function for the two-step MIFARE Classic protocol operations
 *Decrement, Increment and Restore.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode
MIFARE_TwoStepHelper(MFRC522Ptr_t mfrc,
					 uint8_t command,   ///< The command to use
					 uint8_t blockAddr, ///< The block (0-0xff) number.
					 long data			///< The data to transfer in step 2
					 ) {
	StatusCode result;
	uint8_t cmdBuffer[2]; // We only need room for 2 uint8_ts.

	// Step 1: Tell the PICC the command and block address
	cmdBuffer[0] = command;
	cmdBuffer[1] = blockAddr;
	result = PCD_MIFARE_Transceive(
		mfrc, cmdBuffer, 2,
		false); // Adds CRC_A and checks that the response is MF_ACK.
	if (result != STATUS_OK) {
		return result;
	}

	// Step 2: Transfer the data
	result = PCD_MIFARE_Transceive(
		mfrc, (uint8_t *)&data, 4,
		true); // Adds CRC_A and accept timeout as success.
	if (result != STATUS_OK) {
		return result;
	}

	return STATUS_OK;
} // End MIFARE_TwoStepHelper()

/**
 * MIFARE Transfer writes the value stored in the volatile memory into one
 *MIFARE Classic block.
 * For MIFARE Classic only. The sector containing the block must be
 *authenticated before calling this function.
 * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110]
 *or [001].
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode MIFARE_Transfer(MFRC522Ptr_t mfrc,
						   uint8_t blockAddr ///< The block (0-0xff) number.
						   ) {
	StatusCode result;
	uint8_t cmdBuffer[2]; // We only need room for 2 uint8_ts.

	// Tell the PICC we want to transfer the result into block blockAddr.
	cmdBuffer[0] = PICC_CMD_MF_TRANSFER;
	cmdBuffer[1] = blockAddr;
	result = PCD_MIFARE_Transceive(
		mfrc, cmdBuffer, 2,
		false); // Adds CRC_A and checks that the response is MF_ACK.
	if (result != STATUS_OK) {
		return result;
	}
	return STATUS_OK;
} // End MIFARE_Transfer()

/**
 * Helper routine to read the current value from a Value Block.
 *
 * Only for MIFARE Classic and only for blocks in "value block" mode, that
 * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing
 * the block must be authenticated before calling this function.
 *
 * @param[in]   blockAddr   The block (0x00-0xff) number.
 * @param[out]  value       Current value of the Value Block.
 * @return STATUS_OK on success, STATUS_??? otherwise.
  */
StatusCode MIFARE_GetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long *value) {
	StatusCode status;
	uint8_t buffer[18];
	uint8_t size = sizeof(buffer);

	// Read the block
	status = MIFARE_Read(mfrc, 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]);
		//			long l_0 = buffer[0];
		//			long l_8 = buffer[1]<<8;
		//			long l_16 = buffer[2]<<16;
		//			long l_24 = buffer[3]<<24;
		//			*value =l_24 | l_16 | l_8 | l_0;
		*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.
 */
StatusCode MIFARE_SetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long value) {
	uint8_t buffer[18];

	// Translate the long into 4 uint8_ts; 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 uint8_ts 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(mfrc, blockAddr, buffer, 16);
} // End MIFARE_SetValue()

/**
 * Authenticate with a NTAG216.
 *
 * Only for NTAG216. First implemented by Gargantuanman.
 *
 * @param[in]   passWord   password.
 * @param[in]   pACK       result success???.
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode PCD_NTAG216_AUTH(MFRC522Ptr_t mfrc, uint8_t *passWord,
							uint8_t pACK[]) // Authenticate with 32bit password
{
	StatusCode result;
	uint8_t cmdBuffer[18]; // We need room for 16 uint8_ts data and 2 uint8_ts
						   // CRC_A.

	cmdBuffer[0] = 0x1B; // Comando de autentificacion

	for (uint8_t i = 0; i < 4; i++)
		cmdBuffer[i + 1] = passWord[i];

	result = PCD_CalculateCRC(mfrc, cmdBuffer, 5, &cmdBuffer[5]);

	if (result != STATUS_OK) {
		return result;
	}

	// Transceive the data, store the reply in cmdBuffer[]
	uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
	uint8_t cmdBufferSize = sizeof(cmdBuffer);
	uint8_t validBits = 0;
	uint8_t rxlength = 5;
	result =
		PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, cmdBuffer, 7,
								cmdBuffer, &rxlength, &validBits, 0, false);

	pACK[0] = cmdBuffer[0];
	pACK[1] = cmdBuffer[1];

	if (result != STATUS_OK) {
		return result;
	}

	return STATUS_OK;
} // End PCD_NTAG216_AUTH()

/*******************************************************************************
* Support functions
*******************************************************************************/

/**
 * Wrapper for MIFARE protocol communication.
 * Adds CRC_A, executes the Transceive command and checks that the response is
 *MF_ACK or a timeout.
 *
 * @return STATUS_OK on success, STATUS_??? otherwise.
 */
StatusCode
PCD_MIFARE_Transceive(MFRC522Ptr_t mfrc,
					  uint8_t *sendData, ///< Pointer to the data to transfer to
										 ///the FIFO. Do NOT include the CRC_A.
					  uint8_t sendLen,   ///< Number of uint8_ts in sendData.
					  bool acceptTimeout ///< True => A timeout is also success
					  ) {
	//		acceptTimeout = false;

	StatusCode result;
	uint8_t cmdBuffer[18]; // We need room for 16 uint8_ts data and 2 uint8_ts
						   // CRC_A.

	// Sanity check
	if (sendData == NULL || sendLen > 16) {
		return STATUS_INVALID;
	}

	// Copy sendData[] to cmdBuffer[] and add CRC_A
	memcpy(cmdBuffer, sendData, sendLen);
	result = PCD_CalculateCRC(mfrc, cmdBuffer, sendLen, &cmdBuffer[sendLen]);
	if (result != STATUS_OK) {
		return result;
	}
	sendLen += 2;

	// Transceive the data, store the reply in cmdBuffer[]
	uint8_t waitIRq = 0x30; // RxIRq and IdleIRq
	uint8_t cmdBufferSize = sizeof(cmdBuffer);
	uint8_t validBits = 0;
	result = PCD_CommunicateWithPICC(mfrc, PCD_Transceive, waitIRq, cmdBuffer,
									 sendLen, cmdBuffer, &cmdBufferSize,
									 &validBits, 0, false);
	if (acceptTimeout && result == STATUS_TIMEOUT) {
		return STATUS_OK;
	}
	if (result != STATUS_OK) {
		return result;
	}
	// The PICC must reply with a 4 bit ACK
	if (cmdBufferSize != 1 || validBits != 4) {
		return STATUS_ERROR;
	}
	if (cmdBuffer[0] != MF_ACK) {
		return STATUS_MIFARE_NACK;
	}
	return STATUS_OK;
} // End PCD_MIFARE_Transceive()

/**
 * Returns const char pointer to a status code name.
 *
 * @return const char *
 */
const char *GetStatusCodeName(StatusCode code ///< One of theStatusCode enums.
							  ) {
	switch (code) {
	case STATUS_OK:
		return "Success.";
	case STATUS_ERROR:
		return "Error in communication.";
	case STATUS_COLLISION:
		return "Collision detected.";
	case STATUS_TIMEOUT:
		return "Timeout in communication.";
	case STATUS_NO_ROOM:
		return "A buffer is not big enough.";
	case STATUS_INTERNAL_ERROR:
		return "Internal error in the code. Should not happen.";
	case STATUS_INVALID:
		return "Invalid argument.";
	case STATUS_CRC_WRONG:
		return "The CRC_A does not match.";
	case STATUS_MIFARE_NACK:
		return "A MIFARE PICC responded with NAK.";
	default:
		return "Unknown error";
	}
} // End GetStatusCodeName()

/**
 * Translates the SAK (Select Acknowledge) to a PICC type.
 *
 * @return PICC_Type
 */
PICC_Type
PICC_GetType(uint8_t sak ///< The SAK uint8_t 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 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;
	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;
	}
} // End PICC_GetType()

/**
 * Returns const char pointer to the PICC type name.
 *
 * @return const char *
 */
const char *PICC_GetTypeName(PICC_Type piccType ///< One of the PICC_Type enums.
							 ) {
	switch (piccType) {
	case PICC_TYPE_ISO_14443_4:
		return "PICC compliant with ISO/IEC 14443-4";
	case PICC_TYPE_ISO_18092:
		return "PICC compliant with ISO/IEC 18092 (NFC)";
	case PICC_TYPE_MIFARE_MINI:
		return "MIFARE Mini, 320 bytes";
	case PICC_TYPE_MIFARE_1K:
		return "MIFARE 1KB";
	case PICC_TYPE_MIFARE_4K:
		return "MIFARE 4KB";
	case PICC_TYPE_MIFARE_UL:
		return "MIFARE Ultralight or Ultralight C";
	case PICC_TYPE_MIFARE_PLUS:
		return "MIFARE Plus";
	case PICC_TYPE_TNP3XXX:
		return "MIFARE TNP3XXX";
	case PICC_TYPE_NOT_COMPLETE:
		return "SAK indicates UID is not complete.";
	case PICC_TYPE_UNKNOWN:
	default:
		return "Unknown type";
	}
} // End PICC_GetTypeName()

/**
 * Dumps debug info about the connected PCD to Serial.
 * Shows all known firmware versions
 */
void PCD_DumpVersionToSerial(MFRC522Ptr_t mfrc) {
	char string[2];
	// Get the MFRC522 firmware version
	uint8_t v = PCD_ReadRegister(mfrc, VersionReg);
	printf("Firmware Version: 0x");
	// print the hexa value of a unsigned char
	sprintf(string, "%02X", (char)v);
	printf(string);
	// Lookup which version
	switch (v) {
	case 0x88:
		printf(" = (clone)\r\n");
		break;
	case 0x90:
		printf(" = v0.0\r\n");
		break;
	case 0x91:
		printf(" = v1.0\r\n");
		break;
	case 0x92:
		printf(" = v2.0\r\n");
		break;
	default:
		printf(" = (unknown)\r\n");
	}
	// When 0x00 or 0xFF is returned, communication probably failed
	if ((v == 0x00) || (v == 0xFF))
		printf("WARNING: Communication failure, is the MFRC522 properly "
				 "connected?\r\n");
} // End PCD_DumpVersionToSerial()

/**
 * Dumps debug info about the selected PICC to Serial.
 * On success the PICC is halted after dumping the data.
 * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried.
 */
void PICC_DumpToSerial(MFRC522Ptr_t mfrc, Uid *uid ///< Pointer to Uid struct
												   ///returned from a successful
												   ///PICC_Select().
					   ) {
	MIFARE_Key key;

	// Dump UID, SAK and Type
	PICC_DumpDetailsToSerial(uid);

	// Dump contents
	PICC_Type piccType = PICC_GetType(uid->sak);
	switch (piccType) {
	case PICC_TYPE_MIFARE_MINI:
	case PICC_TYPE_MIFARE_1K:
	case PICC_TYPE_MIFARE_4K:
		// All keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
		for (uint8_t i = 0; i < 6; i++) {
			key.keybyte[i] = 0xFF;
		}
		PICC_DumpMifareClassicToSerial(mfrc, uid, piccType, &key);
		break;

	case PICC_TYPE_MIFARE_UL:
		PICC_DumpMifareUltralightToSerial(mfrc);
		break;

	case PICC_TYPE_ISO_14443_4:
	case PICC_TYPE_ISO_18092:
	case PICC_TYPE_MIFARE_PLUS:
	case PICC_TYPE_TNP3XXX:
		printf(
			"Dumping memory contents not implemented for that PICC type.\r\n");
		break;

	case PICC_TYPE_UNKNOWN:
	case PICC_TYPE_NOT_COMPLETE:
	default:
		break; // No memory dump here
	}

	printf("\r\n");
	PICC_HaltA(mfrc); // Already done if it was a MIFARE Classic PICC.
} // End PICC_DumpToSerial()

/**
 * Dumps card info (UID,SAK,Type) about the selected PICC to Serial.
 */
void PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from
									   ///a successful PICC_Select().
							  ) {
	char string[2];
	// UID
	printf("Card UID:");
	for (uint8_t i = 0; i < uid->size; i++) {
		if (uid->uidByte[i] < 0x10)
			printf(" 0");
		else
			printf(" ");

		// print the hexa value of a unsigned char
		sprintf(string, "%02X", (char)uid->uidByte[i]);
		printf(string);
	}
	printf("\r\n");

	// SAK
	printf("Card SAK: ");
	if (uid->sak < 0x10)
		printf("0");

	// print the hexa value of a unsigned char
	sprintf(string, "%02X", (char)uid->sak);
	printf(string);

	// (suggested) PICC type
	PICC_Type piccType = PICC_GetType(uid->sak);
	printf(" PICC type: ");
	printf(PICC_GetTypeName(piccType));
} // End PICC_DumpDetailsToSerial()

/**
 * Dumps memory contents of a MIFARE Classic PICC.
 * On success the PICC is halted after dumping the data.
 */
void PICC_DumpMifareClassicToSerial(
	MFRC522Ptr_t mfrc,
	Uid *uid,			///< Pointer to Uid struct returned from a successful
						///PICC_Select().
	PICC_Type piccType, ///< One of the PICC_Type enums.
	MIFARE_Key *key		///< Key A used for all sectors.
	) {
	uint8_t no_of_sectors = 0;
	switch (piccType) {
	case PICC_TYPE_MIFARE_MINI:
		// Has 5 sectors * 4 blocks/sector * 16 uint8_ts/block = 320 uint8_ts.
		no_of_sectors = 5;
		break;

	case PICC_TYPE_MIFARE_1K:
		// Has 16 sectors * 4 blocks/sector * 16 uint8_ts/block = 1024 uint8_ts.
		no_of_sectors = 16;
		break;

	case PICC_TYPE_MIFARE_4K:
		// Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) *
		// 16 uint8_ts/block = 4096 uint8_ts.
		no_of_sectors = 40;
		break;

	default: // Should not happen. Ignore.
		break;
	}

	// Dump sectors, highest address first.
	if (no_of_sectors) {
		printf("Sector Block   0  1  2  3   4  5  6  7   8  9 10 11  12 13 "
				 "14 15  AccessBits\r\n");
		for (int8_t i = no_of_sectors - 1; i >= 0; i--) {
			PICC_DumpMifareClassicSectorToSerial(mfrc, uid, key, i);
		}
	}
	PICC_HaltA(mfrc); // Halt the PICC before stopping the encrypted session.
	PCD_StopCrypto1(mfrc);
} // End PICC_DumpMifareClassicToSerial()

/**
 * Dumps memory contents of a sector of a MIFARE Classic PICC.
 * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1.
 * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the
 * sector trailer access bits.
 */
void PICC_DumpMifareClassicSectorToSerial(
	MFRC522Ptr_t mfrc,
	Uid *uid,		 ///< Pointer to Uid struct returned from a successful
					 ///PICC_Select().
	MIFARE_Key *key, ///< Key A for the sector.
	uint8_t sector   ///< The sector to dump, 0..39.
	) {
	char string[2];
	uint8_t acces_bit;
	StatusCode status;
	uint8_t firstBlock; // Address of lowest address to dump actually last block
						// dumped)
	uint8_t no_of_blocks; // Number of blocks in sector
	bool isSectorTrailer; // Set to true while handling the "last" (ie highest
						  // address) in the sector.

	// The access bits are stored in a peculiar fashion.
	// There are four groups:
	//		g[3]	Access bits for the sector trailer, block 3 (for sectors 0-31)
	//or block 15 (for sectors 32-39)
	//		g[2]	Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for
	//sectors 32-39)
	//		g[1]	Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for
	//sectors 32-39)
	//		g[0]	Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for
	//sectors 32-39)
	// Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is
	// LSB.
	// The four CX bits are stored together in a nible cx and an inverted nible
	// cx_.
	uint8_t c1, c2, c3;	// Nibbles
	uint8_t c1_, c2_, c3_; // Inverted nibbles
	bool invertedError;	// True if one of the inverted nibbles did not match
	uint8_t g[4];		   // Access bits for each of the four groups.
	uint8_t group;		   // 0-3 - active group for access bits
	bool firstInGroup;	 // True for the first block dumped in the group

	// Determine position and size of sector.
	if (sector < 32) { // Sectors 0..31 has 4 blocks each
		no_of_blocks = 4;
		firstBlock = sector * no_of_blocks;
	} else if (sector < 40) { // Sectors 32-39 has 16 blocks each
		no_of_blocks = 16;
		firstBlock = 128 + (sector - 32) * no_of_blocks;
	} else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors.
		return;
	}

	// Dump blocks, highest address first.
	uint8_t uint8_tCount;
	uint8_t buffer[18];
	uint8_t blockAddr;
	isSectorTrailer = true;
	for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0;
		 blockOffset--) {
		blockAddr = firstBlock + blockOffset;
		// Sector number - only on first line
		if (isSectorTrailer) {
			if (sector < 10)
				printf("   "); // Pad with spaces
			else
				printf("  "); // Pad with spaces
			sprintf(string, "%u", sector);
			printf(string);
			printf("   ");
		} else {
			printf("       ");
		}
		// Block number
		if (blockAddr < 10)
			printf("   "); // Pad with spaces
		else {
			if (blockAddr < 100)
				printf("  "); // Pad with spaces
			else
				printf(" "); // Pad with spaces
		}
		sprintf(string, "%u", blockAddr);
		printf(string);
		printf("  ");
		// Establish encrypted communications before reading the first block
		if (isSectorTrailer) {
			status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A, firstBlock,
									  key, uid);
			if (status != STATUS_OK) {
				printf("PCD_Authenticate() failed: ");
				printf(GetStatusCodeName(status));
				printf("\n");
				return;
			}
		}
		// Read block
		uint8_tCount = sizeof(buffer);
		status = MIFARE_Read(mfrc, blockAddr, buffer, &uint8_tCount);
		if (status != STATUS_OK) {
			printf("MIFARE_Read() failed: ");
			printf(GetStatusCodeName(status));
			continue;
		}
		// Dump data
		for (uint8_t index = 0; index < 16; index++) {
			if (buffer[index] < 0x10)
				printf(" 0");
			else
				printf(" ");

			// print the hexa value of a unsigned char
			sprintf(string, "%02X", (char)buffer[index]);
			printf(string);
			if ((index % 4) == 3) {
				printf(" ");
			}
		}
		// Parse sector trailer data
		if (isSectorTrailer) {
			c1 = buffer[7] >> 4;
			c2 = buffer[8] & 0xF;
			c3 = buffer[8] >> 4;
			c1_ = buffer[6] & 0xF;
			c2_ = buffer[6] >> 4;
			c3_ = buffer[7] & 0xF;
			invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) ||
							(c3 != (~c3_ & 0xF));
			g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0);
			g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1);
			g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2);
			g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3);
			isSectorTrailer = false;
		}

		// Which access group is this block in?
		if (no_of_blocks == 4) {
			group = blockOffset;
			firstInGroup = true;
		} else {
			group = blockOffset / 5;
			firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5);
		}

		if (firstInGroup) {
			// Print access bits
			printf(" [ ");

			acces_bit = (g[group] >> 2) & 1;
			sprintf(string, "%u", acces_bit);
			printf(string);
			printf(" ");

			acces_bit = (g[group] >> 1) & 1;
			sprintf(string, "%u", acces_bit);
			printf(string);
			printf(" ");

			acces_bit = (g[group] >> 0) & 1;
			sprintf(string, "%u", acces_bit);
			printf(string);

			printf(" ] ");
			if (invertedError) {
				printf(" 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]);
			printf(" Value=0x");

			// print the hexa value of a unsigned char
			sprintf(string, "%02X", (char)value);
			printf(string);
			printf(" Adr=0x");

			// print the hexa value of a unsigned char
			sprintf(string, "%02X", (char)buffer[12]);
			printf(string);
		}
		printf("\r\n");
	}

	return;
} // End PICC_DumpMifareClassicSectorToSerial()

/**
 * Dumps memory contents of a MIFARE Ultralight PICC.
 */
void PICC_DumpMifareUltralightToSerial(MFRC522Ptr_t mfrc) {
	char string[2];
	StatusCode status;
	uint8_t uint8_tCount;
	uint8_t buffer[18];
	uint8_t i;

	printf("Page  0  1  2  3\r\n");
	// Try the mpages of the original Ultralight. Ultralight C has more pages.
	for (uint8_t page = 0; page < 16;
		 page += 4) { // Read returns data for 4 pages at a time.
		// Read pages
		uint8_tCount = sizeof(buffer);
		status = MIFARE_Read(mfrc, page, buffer, &uint8_tCount);
		if (status != STATUS_OK) {
			printf("MIFARE_Read() failed: ");
			printf(GetStatusCodeName(status));
			break;
		}
		// Dump data
		for (uint8_t offset = 0; offset < 4; offset++) {
			i = page + offset;
			if (i < 10)
				printf("  "); // Pad with spaces
			else
				printf(" "); // Pad with spaces
			sprintf(string, "%u", i);
			printf(string);
			printf("  ");
			for (uint8_t index = 0; index < 4; index++) {
				i = 4 * offset + index;
				if (buffer[i] < 0x10)
					printf(" 0");
				else
					printf(" ");

				// print the hexa value of a unsigned char
				sprintf(string, "%02X", (char)buffer[i]);
				printf(string);
			}
			printf("\r\n");
		}
	}
} // End PICC_DumpMifareUltralightToSerial()

/**
 * Calculates the bit pattern needed for the specified access bits. In the [C1
 * C2 C3] tuples C1 is MSB (=4) and C3 is LSB (=1).
 */
void MIFARE_SetAccessBits(
	uint8_t *accessBitBuffer, ///< Pointer to uint8_t 6, 7 and 8 in the sector
							  ///trailer. uint8_ts [0..2] will be set.
	uint8_t g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or
				///blocks 0-4 (for sectors 32-39)
	uint8_t g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or
				///blocks 5-9 (for sectors 32-39)
	uint8_t g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or
				///blocks 10-14 (for sectors 32-39)
	uint8_t g3  ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for
				///sectors 0-31) or block 15 (for sectors 32-39)
	) {
	uint8_t c1 =
		((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2);
	uint8_t c2 =
		((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1);
	uint8_t c3 =
		((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0);

	accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF);
	accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF);
	accessBitBuffer[2] = c3 << 4 | c2;
} // End MIFARE_SetAccessBits()

/**
 * Performs the "magic sequence" needed to get Chinese UID changeable
 * Mifare cards to allow writing to sector 0, where the card UID is stored.
 *
 * Note that you do not need to have selected the card through REQA or WUPA,
 * this sequence works immediately when the card is in the reader vicinity.
 * This means you can use this method even on "bricked" cards that your reader
 *does
 * not recognise anymore (see MIFARE_UnbrickUidSector).
 *
 * Of course with non-bricked devices, you're free to select them before calling
 *this function.
 */
bool MIFARE_OpenUidBackdoor(MFRC522Ptr_t mfrc, bool logErrors) {
	char string[2];
	// 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(mfrc); // 50 00 57 CD

	uint8_t cmd = 0x40;
	uint8_t validBits =
		7; /* Our command is only 7 bits. After receiving card response,
		  this will contain amount of valid response bits. */
	uint8_t response[32]; // Card's response is written here
	uint8_t received;
	StatusCode status =
		PCD_TransceiveData(mfrc, &cmd, (uint8_t)1, response, &received,
						   &validBits, (uint8_t)0, false); // 40
	if (status != STATUS_OK) {
		if (logErrors) {
			printf("Card did not respond to 0x40 after HALT command. Are you "
					 "sure it is a UID changeable one?\r\n");
			printf("Error name: ");
			printf(GetStatusCodeName(status));
		}
		return false;
	}
	if (received != 1 || response[0] != 0x0A) {
		if (logErrors) {
			printf("Got bad response on backdoor 0x40 command: ");

			// print the hexa value of a unsigned char
			sprintf(string, "%02X", (char)response[0]);
			printf(string);
			printf(" (");
			sprintf(string, "%u", validBits);
			printf(string);
			printf(" valid bits)\r\n");
		}
		return false;
	}

	cmd = 0x43;
	validBits = 8;
	status = PCD_TransceiveData(mfrc, &cmd, (uint8_t)1, response, &received,
								&validBits, (uint8_t)0, false); // 43
	if (status != STATUS_OK) {
		if (logErrors) {
			printf("Error in communication at command 0x43, after "
					 "successfully executing 0x40\r\n");
			printf("Error name: ");
			printf(GetStatusCodeName(status));
		}
		return false;
	}
	if (received != 1 || response[0] != 0x0A) {
		if (logErrors) {
			printf("Got bad response on backdoor 0x43 command: ");

			// print the hexa value of a unsigned char
			sprintf(string, "%02X", (char)response[0]);
			printf(string);
			printf(" (");
			sprintf(string, "%u", validBits);
			printf(string);
			printf(" 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 MIFARE_SetUid(MFRC522Ptr_t mfrc, uint8_t *newUid, uint8_t uidSize,
				   bool logErrors) {

	// UID + BCC uint8_t can not be larger than 16 together
	if (!newUid || !uidSize || uidSize > 15) {
		if (logErrors) {
			printf("New UID buffer empty, size 0, or size > 15 given\r\n");
		}
		return false;
	}

	// Authenticate for reading
	MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
	StatusCode status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A,
										 (uint8_t)1, &key, &(mfrc->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
			//			  uint8_t atqa_answer[2];
			//			  uint8_t atqa_size = 2;
			//			  PICC_WakeupA(atqa_answer, &atqa_size);

			if (!PICC_IsNewCardPresent(mfrc) || !PICC_ReadCardSerial(mfrc)) {
				printf("No card was previously selected, and none are "
						 "available. Failed to set UID.\r\n");
				return false;
			}

			status = PCD_Authenticate(mfrc, PICC_CMD_MF_AUTH_KEY_A, (uint8_t)1,
									  &key, &(mfrc->uid));
			if (status != STATUS_OK) {
				// We tried, time to give up
				if (logErrors) {
					printf("Failed to authenticate to card for reading, "
							 "could not set UID: \r\n");
					printf(GetStatusCodeName(status));
				}
				return false;
			}
		} else {
			if (logErrors) {
				printf("PCD_Authenticate() failed: ");
				printf(GetStatusCodeName(status));
				printf("\n");
			}
			return false;
		}
	}

	// Read block 0
	uint8_t block0_buffer[18];
	uint8_t uint8_tCount = sizeof(block0_buffer);
	status = MIFARE_Read(mfrc, (uint8_t)0, block0_buffer, &uint8_tCount);
	if (status != STATUS_OK) {
		if (logErrors) {
			printf("MIFARE_Read() failed: ");
			printf(GetStatusCodeName(status));
			printf(
				"Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?\r\n");
		}
		return false;
	}

	// Write new UID to the data we just read, and calculate BCC uint8_t
	uint8_t bcc = 0;
	for (int i = 0; i < uidSize; i++) {
		block0_buffer[i] = newUid[i];
		bcc ^= newUid[i];
	}

	// Write BCC uint8_t to buffer
	block0_buffer[uidSize] = bcc;

	// Stop encrypted traffic so we can send raw uint8_ts
	PCD_StopCrypto1(mfrc);

	// Activate UID backdoor
	if (!MIFARE_OpenUidBackdoor(mfrc, logErrors)) {
		if (logErrors) {
			printf("Activating the UID backdoor failed.\r\n");
		}
		return false;
	}

	// Write modified block 0 back to card
	status = MIFARE_Write(mfrc, (uint8_t)0, block0_buffer, (uint8_t)16);
	if (status != STATUS_OK) {
		if (logErrors) {
			printf("MIFARE_Write() failed: ");
			printf(GetStatusCodeName(status));
		}
		return false;
	}

	// Wake the card up again
	uint8_t atqa_answer[2];
	uint8_t atqa_size = 2;
	PICC_WakeupA(mfrc, atqa_answer, &atqa_size);

	return true;
}

/**
 * Resets entire sector 0 to zeroes, so the card can be read again by readers.
 */
bool MIFARE_UnbrickUidSector(MFRC522Ptr_t mfrc, bool logErrors) {
	MIFARE_OpenUidBackdoor(mfrc, logErrors);

	uint8_t 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
	StatusCode status =
		MIFARE_Write(mfrc, (uint8_t)0, block0_buffer, (uint8_t)16);
	if (status != STATUS_OK) {
		if (logErrors) {
			printf("MIFARE_Write() failed: ");
			printf(GetStatusCodeName(status));
		}
		return false;
	}
	return true;
}

/*******************************************************************************
* Convenience functions - does not add extra functionality
*******************************************************************************/

/**
 * Returns true if a PICC responds to PICC_CMD_REQA.
 * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are
 *ignored.
 *
 * @return bool
 */
bool PICC_IsNewCardPresent(MFRC522Ptr_t mfrc) {
	uint8_t bufferATQA[2];
	uint8_t bufferSize = sizeof(bufferATQA);
	StatusCode result = PICC_RequestA(mfrc, bufferATQA, &bufferSize);
	return (result == STATUS_OK || result == STATUS_COLLISION);
} // End PICC_IsNewCardPresent()

/**
 * Simple wrapper around PICC_Select.
 * Returns true if a UID could be read.
 * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA()
 *first.
 * The read UID is available in the class variable uid.
 *
 * @return bool
 */
bool PICC_ReadCardSerial(MFRC522Ptr_t mfrc) {
	StatusCode result = PICC_Select(mfrc, &(mfrc->uid), 0);
	return (result == STATUS_OK);
} // End

static inline void cs_select(const uint cs) {
    asm volatile("nop \n nop \n nop");
    gpio_put(cs, 0); // Active low
    asm volatile("nop \n nop \n nop");
}

static inline void cs_deselect(const uint cs) {
    asm volatile("nop \n nop \n nop");
    gpio_put(cs, 1);
    asm volatile("nop \n nop \n nop");
}

Nastavení pinů je zde:

pico-mfrc522/mfrc522.h
/*   
 * mfrc522.h
 *
 * mfrc522 library for pi pico c/c++ sdk
 * 
 * Created: 2023
 * Author: Benjamin Modica
 * 
 * This is an adaptation of a c library for PLCOpen made by:
 * https://github.com/luisfg30/rfid Which is an adaptation of the C++ Arduino library , available at:
 * https://github.com/miguelbalboa/rfid.git
 * The original library is released to the public domain.
 *
 */

#ifndef MFRC522_h
#define MFRC522_h

#include <stdint.h>
#include <stdio.h>
#include <string.h> //some functions need NULL to be defined
#include "pico/stdlib.h"
#include "hardware/spi.h"

/*******************************************************************************
 * Types/enumerations/variables
 ******************************************************************************/
// send only one byte per transfer, see WriteRegister functions
#define BUFFER_SIZE  1 
// Defined as 4MHz in the original library
#define MFRC522_BIT_RATE 4000000 
// Used for ADT object allocation
#define MFRC_MAX_INSTANCES 2	 
// Reset pin to MFRC522
#define RESET_PIN 20

static const uint8_t FIFO_SIZE = 64; // Size of the MFRC522 FIFO

static const uint cs_pin = 17;
static const uint sck_pin = 18;
static const uint mosi_pin = 19;
static const uint miso_pin = 16;

static const uint8_t SELF_TEST_BYTES[] = {
	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
};

//0x2F

/**
 * MFRC522 registers. Described in chapter 9 of the datasheet.
 * When using SPI all addresses are shifted one bit left in the "SPI address
 * (section 8.1.2.3)
 */
enum PCD_Register {
	// Page 0: Command and status
	//						  0x00			// reserved for future use
	CommandReg = 0x01 << 1, // starts and stops command execution
	ComIEnReg = 0x02 << 1,  // enable and disable interrupt request control bits
	DivIEnReg = 0x03 << 1,  // enable and disable interrupt request control bits
	ComIrqReg = 0x04 << 1,  // interrupt request bits
	DivIrqReg = 0x05 << 1,  // interrupt request bits
	ErrorReg = 0x06 << 1,   // error bits showing the error status of the last
							// command executed
	Status1Reg = 0x07 << 1, // communication status bits
	Status2Reg = 0x08 << 1, // receiver and transmitter status bits
	FIFODataReg = 0x09 << 1,   // input and output of 64 byte FIFO buffer
	FIFOLevelReg = 0x0A << 1,  // number of bytes stored in the FIFO buffer
	WaterLevelReg = 0x0B << 1, // level for FIFO underflow and overflow warning
	ControlReg = 0x0C << 1,	// miscellaneous control registers
	BitFramingReg = 0x0D << 1, // adjustments for bit-oriented frames
	CollReg = 0x0E << 1, // bit position of the first bit-collision detected on
						 // the RF interface
	//						  0x0F			// reserved for future use

	// 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
	RxModeReg = 0x13 << 1,	// defines reception data rate and framing
	TxControlReg = 0x14 << 1, // controls the logical behavior of the antenna
							  // driver pins TX1 and TX2
	TxASKReg = 0x15 << 1, // controls the setting of the transmission modulation
	TxSelReg = 0x16 << 1, // selects the internal sources for the antenna driver
	RxSelReg = 0x17 << 1, // selects internal receiver settings
	RxThresholdReg = 0x18 << 1, // selects thresholds for the bit decoder
	DemodReg = 0x19 << 1,		// defines demodulator settings
	// 						  0x1A			// reserved for future use
	// 						  0x1B			// reserved for future use
	MfTxReg =
		0x1C << 1, // controls some MIFARE communication transmit parameters
	MfRxReg =
		0x1D << 1, // controls some MIFARE communication receive parameters
	// 						  0x1E			// reserved for future use
	SerialSpeedReg =
		0x1F << 1, // selects the speed of the serial UART interface

	// Page 2: Configuration
	// 						  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
	ModWidthReg = 0x24 << 1, // controls the ModWidth setting?
	// 						  0x25			// reserved for future use
	RFCfgReg = 0x26 << 1,  // configures the receiver gain
	GsNReg = 0x27 << 1,	// selects the conductance of the antenna driver pins
						   // TX1 and TX2 for modulation
	CWGsPReg = 0x28 << 1,  // defines the conductance of the p-driver output
						   // during periods of no modulation
	ModGsPReg = 0x29 << 1, // defines the conductance of the p-driver output
						   // during periods of modulation
	TModeReg = 0x2A << 1,  // defines settings for the internal timer
	TPrescalerReg = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The
							   // 4 high bits are in TModeReg.
	TReloadRegH = 0x2C << 1,   // defines the 16-bit timer reload value
	TReloadRegL = 0x2D << 1,
	TCounterValueRegH = 0x2E << 1, // shows the 16-bit timer value
	TCounterValueRegL = 0x2F << 1,

	// Page 3: Test Registers
	// 						  0x30			// reserved for future use
	TestSel1Reg = 0x31 << 1,  // general test signal configuration
	TestSel2Reg = 0x32 << 1,  // general test signal configuration
	TestPinEnReg = 0x33 << 1, // enables pin output driver on pins D1 to D7
	TestPinValueReg =
		0x34
		<< 1, // defines the values for D1 to D7 when it is used as an I/O bus
	TestBusReg = 0x35 << 1,	// shows the status of the internal test bus
	AutoTestReg = 0x36 << 1,   // controls the digital self-test
	VersionReg = 0x37 << 1,	// shows the software version
	AnalogTestReg = 0x38 << 1, // controls the pins AUX1 and AUX2
	TestDAC1Reg = 0x39 << 1,   // defines the test value for TestDAC1
	TestDAC2Reg = 0x3A << 1,   // defines the test value for TestDAC2
	TestADCReg = 0x3B << 1	 // shows the value of ADC I and Q channels
	// 						  0x3C			// reserved for production tests
	// 						  0x3D			// reserved for production tests
	// 						  0x3E			// reserved for production tests
	// 						  0x3F			// reserved for production tests
};

/**
 * MFRC522 commands. Described in chapter 10 of the datasheet.
 */
typedef enum _PCD_Command {
	PCD_Idle = 0x00,			 // no action, cancels current command execution
	PCD_Mem = 0x01,				 // stores 25 bytes into the internal buffer
	PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number
	PCD_CalcCRC = 0x03, // activates the CRC co-processor or performs a self-test
	PCD_Transmit = 0x04,	// transmits data from the FIFO buffer
	PCD_NoCmdChange = 0x07, // no command change, can be used to modify the
							// CommandReg register bits without affecting the
							// command, for example, the PowerDown bit
	PCD_Receive = 0x08,		// activates the receiver circuits
	PCD_Transceive = 0x0C,  // transmits data from FIFO buffer to antenna and
							// automatically activates the receiver after
							// transmission
	PCD_MFAuthent =
		0x0E, // performs the MIFARE standard authentication as a reader
	PCD_SoftReset = 0x0F, // resets the MFRC522
    PCD_EnableSelfTest = 0x09
} PCD_Command;

/**
 * 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. 
 */
typedef 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
} PCD_RxGain;

/**
 * Commands sent to the PICC.
 */
typedef enum _PICC_Command {
	// The commands used by the PCD to manage communication with several PICCs
	// (ISO 14443-3, Type A, section 6.4)
	PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state
						  // IDLE to go to READY and prepare for anti collision
						  // or selection. 7 bit frame.
	PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state
						  // IDLE and HALT to go to READY(*) and prepare for
						  // anti collision 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 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.mouser.com/ds/2/302/MF1S503x-89574.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.
	// The read/write commands can also be used for MIFARE Ultralight.
	PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A
	PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B
	PICC_CMD_MF_READ = 0x30,	   // Reads one 16 byte block from the
	// authenticated sector of the PICC. Also used for
	// MIFARE Ultralight.
	PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the
							  // authenticated sector of the PICC. Called
							  // "COMPATIBILITY WRITE" for MIFARE Ultralight.
	PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and
								  // stores the result in the internal data
								  // register.
	PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and
								  // stores the result in the internal data
								  // register.
	PICC_CMD_MF_RESTORE =
		0xC2, // Reads the contents of a block into the internal data register.
	PICC_CMD_MF_TRANSFER =
		0xB0, // Writes the contents of the internal data register to a block.
	// The commands used for MIFARE Ultralight (from
	// http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
	// The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE
	// Ultralight.
	PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC.
} PICC_Command;

// MIFARE constants that does not fit anywhere else
typedef enum _MIFARE_Misc {
	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.
} MIFARE_Misc;

// PICC types we can detect. Remember to update PICC_GetTypeName() if you add
// more.
// last value set to 0xff, then compiler uses less ram, it seems some
// optimizations are triggered
typedef enum _PICC_Type {
	PICC_TYPE_UNKNOWN,
	PICC_TYPE_ISO_14443_4,		  // PICC compliant with ISO/IEC 14443-4
	PICC_TYPE_ISO_18092,		  // PICC compliant with ISO/IEC 18092 (NFC)
	PICC_TYPE_MIFARE_MINI,		  // MIFARE Classic protocol, 320 bytes
	PICC_TYPE_MIFARE_1K,		  // MIFARE Classic protocol, 1KB
	PICC_TYPE_MIFARE_4K,		  // MIFARE Classic protocol, 4KB
	PICC_TYPE_MIFARE_UL,		  // MIFARE Ultralight or Ultralight C
	PICC_TYPE_MIFARE_PLUS,		  // MIFARE Plus
	PICC_TYPE_TNP3XXX,			  // Only mentioned in NXP AN 10833 MIFARE Type
								  // Identification Procedure
	PICC_TYPE_NOT_COMPLETE = 0xff // SAK indicates UID is not complete.
} PICC_Type;

// Return codes from the functions in this class. Remember to update
// GetStatusCodeName() if you add more.
// last value set to 0xff, then compiler uses less ram, it seems some
// optimizations are triggered
typedef enum _StatusCode {
	STATUS_OK,			   // Success
	STATUS_ERROR,		   // Error in communication
	STATUS_COLLISION,	  // Collision detected
	STATUS_TIMEOUT,		   // Timeout in communication.
	STATUS_NO_ROOM,		   // A buffer is not big enough.
	STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-)
	STATUS_INVALID,		   // Invalid argument.
	STATUS_CRC_WRONG,	  // The CRC_A does not match
	STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK.
} StatusCode;

// A struct used for passing the UID of a PICC.
typedef struct {
	uint8_t size; // Number of bytes in the UID. 4, 7 or 10.
	uint8_t uidByte[10];
	uint8_t sak; // The SAK (Select acknowledge) byte returned from the PICC
				 // after successful selection.
} Uid;

// A struct used for passing a MIFARE Crypto1 key
typedef struct { uint8_t keybyte[MF_KEY_SIZE]; } MIFARE_Key;

// A struct used to set GPIO pins of LPCXpresso4337 
typedef struct {
	uint8_t port;
	uint8_t pin;
} io_port_t;

// A struct used to define a MFRC522 ADT object, useful when using more than one
struct MFRC522_T {
	Uid uid; // Used by PICC_ReadCardSerial().
	// Variables used in the SSP(SPI) peripheral of the board
	spi_inst_t *spi; // Select SSP0 or SSP1
	uint _chipSelectPin; // = {1, 8}; // As default example use GPIO1[8]= P1_5
	uint8_t Tx_Buf[BUFFER_SIZE];
	uint8_t Rx_Buf[BUFFER_SIZE];
};

// Pointer to a MFRC5222 ADT object
typedef struct MFRC522_T *MFRC522Ptr_t;

/**
 * Function to setup a MFRC522 ADT object
 * @return an initialized  ADT object
 */
MFRC522Ptr_t MFRC522_Init();

/*******************************************************************************
* Basic interface functions for communicating with the MFRC522
*******************************************************************************/
void PCD_WriteRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t value);
void PCD_WriteNRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t count,
						uint8_t *values);
uint8_t PCD_ReadRegister(MFRC522Ptr_t mfrc, uint8_t reg);
void PCD_ReadNRegister(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t count,
					   uint8_t *values, uint8_t rxAlign);
void setBitMask(unsigned char reg, unsigned char mask);
void PCD_SetRegisterBitMask(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t mask);
void PCD_ClearRegisterBitMask(MFRC522Ptr_t mfrc, uint8_t reg, uint8_t mask);
StatusCode PCD_CalculateCRC(MFRC522Ptr_t mfrc, uint8_t *data, uint8_t length,
							uint8_t *result);
							
//Chip select for pi pico SPI
static inline void cs_select(const uint cs);
static inline void cs_deselect(const uint cs); 

/*******************************************************************************
* Functions for manipulating the MFRC522
*******************************************************************************/
void PCD_Init(MFRC522Ptr_t mfrc, spi_inst_t *spi);
void PCD_Reset(MFRC522Ptr_t mfrc);
void PCD_AntennaOn(MFRC522Ptr_t mfrc);
void PCD_AntennaOff(MFRC522Ptr_t mfrc);
uint8_t PCD_GetAntennaGain(MFRC522Ptr_t mfrc);
void PCD_SetAntennaGain(MFRC522Ptr_t mfrc, uint8_t mask);
uint8_t PCD_SelfTest(MFRC522Ptr_t mfrc);

/*******************************************************************************
* Functions for communicating with PICCs
*******************************************************************************/
StatusCode PCD_TransceiveData(MFRC522Ptr_t mfrc, uint8_t *sendData,
							  uint8_t sendLen, uint8_t *backData,
							  uint8_t *backLen, uint8_t *validBits,
							  uint8_t rxAlign, bool checkCRC);
StatusCode PCD_CommunicateWithPICC(MFRC522Ptr_t mfrc, uint8_t command,
								   uint8_t waitIRq, uint8_t *sendData,
								   uint8_t sendLen, uint8_t *backData,
								   uint8_t *backLen, uint8_t *validBits,
								   uint8_t rxAlign, bool checkCRC);
StatusCode PICC_RequestA(MFRC522Ptr_t mfrc, uint8_t *bufferATQA,
						 uint8_t *bufferSize);
StatusCode PICC_WakeupA(MFRC522Ptr_t mfrc, uint8_t *bufferATQA,
						uint8_t *bufferSize);
StatusCode PICC_REQA_or_WUPA(MFRC522Ptr_t mfrc, uint8_t command,
							 uint8_t *bufferATQA, uint8_t *bufferSize);
StatusCode PICC_Select(MFRC522Ptr_t mfrc, Uid *uid, uint8_t validBits);
StatusCode PICC_HaltA(MFRC522Ptr_t mfrc);

/*******************************************************************************
*Functions for communicating with MIFARE PICCs
*******************************************************************************/
StatusCode PCD_Authenticate(MFRC522Ptr_t mfrc, uint8_t command,
							uint8_t blockAddr, MIFARE_Key *key, Uid *uid);
void PCD_StopCrypto1(MFRC522Ptr_t mfrc);
StatusCode MIFARE_Read(MFRC522Ptr_t mfrc, uint8_t blockAddr, uint8_t *buffer,
					   uint8_t *bufferSize);
StatusCode MIFARE_Write(MFRC522Ptr_t mfrc, uint8_t blockAddr, uint8_t *buffer,
						uint8_t bufferSize);
StatusCode MIFARE_Ultralight_Write(MFRC522Ptr_t mfrc, uint8_t page,
								   uint8_t *buffer, uint8_t bufferSize);
StatusCode MIFARE_Decrement(MFRC522Ptr_t mfrc, uint8_t blockAddr, long delta);
StatusCode MIFARE_Increment(MFRC522Ptr_t mfrc, uint8_t blockAddr, long delta);
StatusCode MIFARE_Restore(MFRC522Ptr_t mfrc, uint8_t blockAddr);
StatusCode MIFARE_Transfer(MFRC522Ptr_t mfrc, uint8_t blockAddr);
StatusCode MIFARE_GetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long *value);
StatusCode MIFARE_SetValue(MFRC522Ptr_t mfrc, uint8_t blockAddr, long value);
StatusCode PCD_NTAG216_AUTH(MFRC522Ptr_t mfrc, uint8_t *passWord,
							uint8_t pACK[]);

/*******************************************************************************
* Support functions
*******************************************************************************/
StatusCode PCD_MIFARE_Transceive(MFRC522Ptr_t mfrc, uint8_t *sendData,
								 uint8_t sendLen, bool acceptTimeout);
const char *GetStatusCodeName(StatusCode code);
const char *PICC_GetTypeName(PICC_Type type);
StatusCode MIFARE_TwoStepHelper(MFRC522Ptr_t mfrc, uint8_t command,
								uint8_t blockAddr, long data);

// Support functions for debugging
void PCD_DumpVersionToSerial(MFRC522Ptr_t mfrc);
void PICC_DumpToSerial(MFRC522Ptr_t mfrc, Uid *uid);
void PICC_DumpDetailsToSerial(Uid *uid);
void PICC_DumpMifareClassicToSerial(MFRC522Ptr_t mfrc, Uid *uid,
									PICC_Type piccType, MIFARE_Key *key);
void PICC_DumpMifareClassicSectorToSerial(MFRC522Ptr_t mfrc, Uid *uid,
										  MIFARE_Key *key, uint8_t sector);
void PICC_DumpMifareUltralightToSerial(MFRC522Ptr_t mfrc);

// Advanced functions for MIFARE
void MIFARE_SetAccessBits(uint8_t *accessBitBuffer, uint8_t g0, uint8_t g1,
						  uint8_t g2, uint8_t g3);
bool MIFARE_OpenUidBackdoor(MFRC522Ptr_t mfrc, bool logErrors);
bool MIFARE_SetUid(MFRC522Ptr_t mfrc, uint8_t *newUid, uint8_t uidSize,
				   bool logErrors);
bool MIFARE_UnbrickUidSector(MFRC522Ptr_t mfrc, bool logErrors);

/*******************************************************************************
* Convenience functions - does not add extra functionality
*******************************************************************************/
bool PICC_IsNewCardPresent(MFRC522Ptr_t mfrc);
bool PICC_ReadCardSerial(MFRC522Ptr_t mfrc);

#endif
Čtení karty
Selecting card
PICC dump:
Card UID: 19 009 29 3E
Card SAK: 008 PICC type: MIFARE 1KBSector Block   0  1  2  3   4  5  6  7   8  9 10 11  12 13 14 15  AccessBits
  15     63   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         62   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         61   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         60   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
  14     59   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         58   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         57   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         56   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
  13     55   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         54   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         53   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         52   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
  12     51   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         50   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         49   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         48   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
  11     47   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         46   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         45   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         44   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
  10     43   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         42   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         41   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         40   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   9     39   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         38   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         37   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         36   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   8     35   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         34   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         33   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         32   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   7     31   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         30   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         29   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         28   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   6     27   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         26   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         25   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         24   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   5     23   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         22   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         21   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         20   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   4     19   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         18   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         17   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         16   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   3     15   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         14   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         13   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
         12   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   2     11   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
         10   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          9   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          8   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   1      7   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
          6   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          5   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          4   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
   0      3   000 000 000 000  000 000 FF 007  80 69 FF FF  FF FF FF FF  [ 0 0 1 ]
          2   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          1   000 000 000 000  000 000 000 000  000 000 000 000  000 000 000 000  [ 0 0 0 ]
          0   19 009 29 3E  007 008 004 000  62 63 64 65  66 67 68 69  [ 0 0 0 ]

Uid is: 19 9 29 3e
Authentication Success
Waiting for card

Zdroje a odkazy