This is a cost effective alert system that uses radio frequency identification. RC522 module and 13.56MHz RFID tags are used. Differentiates between registered and unregistered users based on UID of the tag, then displays alerts in serial terminal. The code presently has two registered users; can be easily expanded to have more registered users.

Dependencies:   MFRC522 mbed

MFRC522.h

Committer:
nivmukka
Date:
2016-03-27
Revision:
1:d950cea9271b
Parent:
0:1026a98e87f4

File content as of revision 1:d950cea9271b:

/**
 * MFRC522.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT.
 * Based on code Dr.Leong   ( WWW.B2CQSHOP.COM )
 * Created by Miguel Balboa (circuitito.com), Jan, 2012.
 * Rewritten by Soren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.)
 * Ported to mbed by Martin Olejar, Dec, 2013
 *
 * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions.
 * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board.
 *
 * There are three hardware components involved:
 * 1) The micro controller: An Arduino
 * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC
 * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203.
 *
 * The microcontroller and card reader uses SPI for communication.
 * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf
 *
 * The card reader and the tags communicate using a 13.56MHz electromagnetic field.
 * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision".
 * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf
 * Details are found in chapter 6, Type A: Initialization and anticollision.
 *
 * If only the PICC UID is wanted, the above documents has all the needed information.
 * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected.
 * The MIFARE Classic chips and protocol is described in the datasheets:
 *    1K:   http://www.nxp.com/documents/data_sheet/MF1S503x.pdf
 *    4K:   http://www.nxp.com/documents/data_sheet/MF1S703x.pdf
 *    Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf
 * The MIFARE Ultralight chip and protocol is described in the datasheets:
 *    Ultralight:   http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf
 *    Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf
 *
 * MIFARE Classic 1K (MF1S503x):
 *    Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes.
 *    The blocks are numbered 0-63.
 *    Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7:
 *        Bytes 0-5:   Key A
 *        Bytes 6-8:   Access Bits
 *        Bytes 9:     User data
 *        Bytes 10-15: Key B (or user data)
 *    Block 0 is read only manufacturer data.
 *    To access a block, an authentication using a key from the block's sector must be performed first.
 *    Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11).
 *    All keys are set to FFFFFFFFFFFFh at chip delivery.
 *    Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked.
 *    To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns.
 * MIFARE Classic 4K (MF1S703x):
 *    Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes.
 *    The blocks are numbered 0-255.
 *    The last block in each sector is the Sector Trailer like above.
 * MIFARE Classic Mini (MF1 IC S20):
 *    Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes.
 *    The blocks are numbered 0-19.
 *    The last block in each sector is the Sector Trailer like above.
 *
 * MIFARE Ultralight (MF0ICU1):
 *    Has 16 pages of 4 bytes = 64 bytes.
 *    Pages 0 + 1 is used for the 7-byte UID.
 *    Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2)
 *    Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0.
 *    Pages 4-15 are read/write unless blocked by the lock bytes in page 2.
 * MIFARE Ultralight C (MF0ICU2):
 *    Has 48 pages of 4 bytes = 64 bytes.
 *    Pages 0 + 1 is used for the 7-byte UID.
 *    Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2)
 *    Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0.
 *    Pages 4-39 are read/write unless blocked by the lock bytes in page 2.
 *    Page 40 Lock bytes
 *    Page 41 16 bit one way counter
 *    Pages 42-43 Authentication configuration
 *    Pages 44-47 Authentication key
 */
#ifndef MFRC522_h
#define MFRC522_h

#include "mbed.h"

class MFRC522 {
public:

  /**
   * MFRC522 registers (described in chapter 9 of the datasheet).
   * When using SPI all addresses are shifted one bit left in the "SPI address byte" (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,
    TCntValueRegH   = 0x2E << 1,  // shows the 16-bit timer value
    TCntValueRegL   = 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.
  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 coprocessor 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
  };

  // Commands sent to the PICC.
  enum PICC_Command {
    // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
    PICC_CMD_REQA          = 0x26,   // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision 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 anticollision or selection. 7 bit frame.
    PICC_CMD_CT            = 0x88,   // Cascade Tag. Not really a command, but used during anti collision.
    PICC_CMD_SEL_CL1       = 0x93,   // Anti collision/Select, Cascade Level 1
    PICC_CMD_SEL_CL2       = 0x95,   // Anti collision/Select, Cascade Level 1
    PICC_CMD_SEL_CL3       = 0x97,   // Anti collision/Select, Cascade Level 1
    PICC_CMD_HLTA          = 0x50,   // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.

    // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9)
    // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector.
    // 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.
  };

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

  // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more.
  enum PICC_Type {
    PICC_TYPE_UNKNOWN      = 0,
    PICC_TYPE_ISO_14443_4  = 1,  // PICC compliant with ISO/IEC 14443-4
    PICC_TYPE_ISO_18092    = 2,  // PICC compliant with ISO/IEC 18092 (NFC)
    PICC_TYPE_MIFARE_MINI  = 3,  // MIFARE Classic protocol, 320 bytes
    PICC_TYPE_MIFARE_1K    = 4,  // MIFARE Classic protocol, 1KB
    PICC_TYPE_MIFARE_4K    = 5,  // MIFARE Classic protocol, 4KB
    PICC_TYPE_MIFARE_UL    = 6,  // MIFARE Ultralight or Ultralight C
    PICC_TYPE_MIFARE_PLUS  = 7,  // MIFARE Plus
    PICC_TYPE_TNP3XXX      = 8,  // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure
    PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete.
  };

  // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more.
  enum StatusCode {
    STATUS_OK              = 1,  // Success
    STATUS_ERROR           = 2,  // Error in communication
    STATUS_COLLISION       = 3,  // Collision detected
    STATUS_TIMEOUT         = 4,  // Timeout in communication.
    STATUS_NO_ROOM         = 5,  // A buffer is not big enough.
    STATUS_INTERNAL_ERROR  = 6,  // Internal error in the code. Should not happen ;-)
    STATUS_INVALID         = 7,  // Invalid argument.
    STATUS_CRC_WRONG       = 8,  // The CRC_A does not match
    STATUS_MIFARE_NACK     = 9   // A MIFARE PICC responded with NAK.
  };

  // 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;

  // Member variables
  Uid uid;                // Used by PICC_ReadCardSerial().

  // Size of the MFRC522 FIFO
  static const uint8_t FIFO_SIZE = 64;   // The FIFO is 64 bytes.

  /**
  * MFRC522 constructor
  *
  * @param mosi  SPI MOSI pin
  * @param miso  SPI MISO pin
  * @param sclk  SPI SCLK pin
  * @param cs    SPI CS pin
  * @param reset Reset pin
  */
  MFRC522(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName reset);

  /**
  * MFRC522 destructor
  */
  ~MFRC522();
  

  // ************************************************************************************
  //! @name Functions for manipulating the MFRC522
  // ************************************************************************************
  //@{

  /**
  * Initializes the MFRC522 chip.
  */
  void    PCD_Init           (void);

  /**
  * Performs a soft reset on the MFRC522 chip and waits for it to be ready again.
  */
  void    PCD_Reset          (void);

  /**
  * Turns the antenna on by enabling pins TX1 and TX2.
  * After a reset these pins disabled.
  */
  void    PCD_AntennaOn      (void);

  /**
  * Writes a byte to the specified register in the MFRC522 chip.
  * The interface is described in the datasheet section 8.1.2.
  *
  * @param reg   The register to write to. One of the PCD_Register enums.
  * @param value The value to write.
  */
  void    PCD_WriteRegister  (uint8_t reg, uint8_t value);

  /**
  * Writes a number of bytes to the specified register in the MFRC522 chip.
  * The interface is described in the datasheet section 8.1.2.
  *
  * @param reg    The register to write to. One of the PCD_Register enums.
  * @param count  The number of bytes to write to the register
  * @param values The values to write. Byte array.
  */
  void    PCD_WriteRegister  (uint8_t reg, uint8_t count, uint8_t *values);

  /**
  * Reads a byte from the specified register in the MFRC522 chip.
  * The interface is described in the datasheet section 8.1.2.
  *
  * @param reg The register to read from. One of the PCD_Register enums.
  * @returns Register value
  */
  uint8_t PCD_ReadRegister   (uint8_t reg);

  /**
  * Reads a number of bytes from the specified register in the MFRC522 chip.
  * The interface is described in the datasheet section 8.1.2.
  *
  * @param reg     The register to read from. One of the PCD_Register enums.
  * @param count   The number of bytes to read.
  * @param values  Byte array to store the values in.
  * @param rxAlign Only bit positions rxAlign..7 in values[0] are updated.
  */
  void    PCD_ReadRegister   (uint8_t reg, uint8_t count, uint8_t *values, uint8_t rxAlign = 0);

  /**
  * Sets the bits given in mask in register reg.
  *
  * @param reg  The register to update. One of the PCD_Register enums.
  * @param mask The bits to set.
  */
  void    PCD_SetRegisterBits(uint8_t reg, uint8_t mask);

  /**
  * Clears the bits given in mask from register reg.
  *
  * @param reg  The register to update. One of the PCD_Register enums.
  * @param mask The bits to clear.
  */
  void    PCD_ClrRegisterBits(uint8_t reg, uint8_t mask);

  /**
  * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
  *
  * @param data   Pointer to the data to transfer to the FIFO for CRC calculation.
  * @param length The number of bytes to transfer.
  * @param result Pointer to result buffer. Result is written to result[0..1], low byte first.
  * @return STATUS_OK on success, STATUS_??? otherwise.
  */
  uint8_t PCD_CalculateCRC   (uint8_t *data, uint8_t length, uint8_t *result);

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


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

  /**
   * 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.
   *
   * @param bufferATQA  The buffer to store the ATQA (Answer to request) in
   * @param bufferSize  Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
   * 
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t PICC_RequestA      (uint8_t *bufferATQA, uint8_t *bufferSize);
  
  /**
   * 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.
   *
   * @param bufferATQA  The buffer to store the ATQA (Answer to request) in
   * @param bufferSize  Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
   * 
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */  
  uint8_t PICC_WakeupA       (uint8_t *bufferATQA, uint8_t *bufferSize);
  
  /**
   * 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.
   *
   * @param command     The command to send - PICC_CMD_REQA or PICC_CMD_WUPA
   * @param bufferATQA  The buffer to store the ATQA (Answer to request) in
   * @param bufferSize  Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK.
   * 
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */  
  uint8_t PICC_REQA_or_WUPA  (uint8_t command, uint8_t *bufferATQA, uint8_t *bufferSize);
  
  /**
   * Transmits SELECT/ANTICOLLISION commands to select a single PICC.
   * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA().
   * On success:
   *   - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.)
   *   - The UID size and value of the chosen PICC is returned in *uid along with the SAK.
   * 
   * A PICC UID consists of 4, 7 or 10 bytes.
   * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used:
   *
   *   UID size        Number of UID bytes                Cascade levels                Example of PICC
   *   ========        ===================                ==============                ===============
   *   single                   4                                1                      MIFARE Classic
   *   double                   7                                2                      MIFARE Ultralight
   *   triple                  10                                3                      Not currently in use?
   *
   *
   * @param uid        Pointer to Uid struct. Normally output, but can also be used to supply a known UID.
   * @param validBits  The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size.
   *   
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t PICC_Select        (Uid *uid, uint8_t validBits = 0);
  
  /**
   * Instructs a PICC in state ACTIVE(*) to go to state HALT.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */  
  uint8_t PICC_HaltA         (void);
  
  // ************************************************************************************
  //@}


  // ************************************************************************************
  //! @name 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.
   *
   * @param command    PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B
   * @param blockAddr  The block number. See numbering in the comments in the .h file.
   * @param key        Pointer to the Crypteo1 key to use (6 bytes)
   * @param uid        Pointer to Uid struct. The first 4 bytes of the UID is used.
   * 
   * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key.
   */
  uint8_t PCD_Authenticate   (uint8_t command, uint8_t blockAddr, MIFARE_Key *key, Uid *uid);
  
  /**
   * 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    (void);
  
  /**
   * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC.
   * 
   * For MIFARE Classic the sector containing the block must be authenticated before calling this function.
   * 
   * For MIFARE Ultralight only addresses 00h to 0Fh are decoded.
   * The MF0ICU1 returns a NAK for higher addresses.
   * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument.
   * For example; if blockAddr is 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 bytes because a CRC_A is also returned.
   * Checks the CRC_A before returning STATUS_OK.
   *
   * @param blockAddr  MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from.
   * @param buffer     The buffer to store the data in
   * @param bufferSize Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_Read        (uint8_t blockAddr, uint8_t *buffer, uint8_t *bufferSize);
  
  /**
   * Writes 16 bytes to the active PICC.
   * 
   * For MIFARE Classic the sector containing the block must be authenticated before calling this function.
   * 
   * For MIFARE Ultralight the opretaion is called "COMPATIBILITY WRITE".
   * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3)
   * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0.
   *
   * @param blockAddr  MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to.
   * @param buffer     The 16 bytes to write to the PICC
   * @param bufferSize Buffer size, must be at least 16 bytes. Exactly 16 bytes are written.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
 */
  uint8_t MIFARE_Write       (uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize);
  
  /**
   * Writes a 4 byte page to the active MIFARE Ultralight PICC.
   * 
   * @param page       The page (2-15) to write to.
   * @param buffer     The 4 bytes to write to the PICC
   * @param bufferSize Buffer size, must be at least 4 bytes. Exactly 4 bytes are written.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_UltralightWrite(uint8_t page, uint8_t *buffer, uint8_t bufferSize);
   
  /**
   * 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.
   *
   * @param blockAddr The block (0-0xff) number.
   * @param delta     This number is subtracted from the value of block blockAddr.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_Decrement   (uint8_t blockAddr, uint32_t delta);
  
  /**
   * 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.
   * 
   * @param blockAddr The block (0-0xff) number.
   * @param delta     This number is added to the value of block blockAddr.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_Increment   (uint8_t blockAddr, uint32_t delta);
  
  /**
   * 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.
   * 
   * @param blockAddr The block (0-0xff) number.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_Restore     (uint8_t blockAddr);
  
  /**
   * 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].
   * 
   * @param blockAddr The block (0-0xff) number.
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_Transfer    (uint8_t blockAddr);
  
  // ************************************************************************************
  //@}


  // ************************************************************************************
  //! @name 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.
   * 
   * @param sendData      Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A.
   * @param sendLen       Number of bytes in sendData.
   * @param acceptTimeout True => A timeout is also success
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t PCD_MIFARE_Transceive(uint8_t *sendData, uint8_t sendLen, bool acceptTimeout = false);
  
  /**
   * Translates the SAK (Select Acknowledge) to a PICC type.
   * 
   * @param sak The SAK byte returned from PICC_Select().
   *
   * @return PICC_Type
   */
  uint8_t PICC_GetType         (uint8_t sak);
  
  /**
   * Returns a string pointer to the PICC type name.
   * 
   * @param type One of the PICC_Type enums.
   *
   * @return A string pointer to the PICC type name.
   */
  char*   PICC_GetTypeName     (uint8_t type);
  
  /**
   * Returns a string pointer to a status code name.
   * 
   * @param code One of the StatusCode enums.
   *
   * @return A string pointer to a status code name.
   */
  char*   GetStatusCodeName    (uint8_t code);
  
  /**
   * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1).
   * 
   * @param accessBitBuffer Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set.
   * @param g0              Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
   * @param g1              Access bits [C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
   * @param g2              Access bits [C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
   * @param g3              Access bits [C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
   */
  void    MIFARE_SetAccessBits (uint8_t *accessBitBuffer,
                                uint8_t g0,
                                uint8_t g1,
                                uint8_t g2,
                                uint8_t g3);
                                
  // ************************************************************************************
  //@}


  // ************************************************************************************
  //! @name 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(void);
  
  /**
   * 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  (void);
  
  // ************************************************************************************
  //@}


private:
  SPI              m_SPI;
  DigitalOut       m_CS;
  DigitalOut       m_RESET;

  /**
   * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore.
   * 
   * @param command    The command to use
   * @param blockAddr  The block (0-0xff) number.
   * @param data       The data to transfer in step 2
   *
   * @return STATUS_OK on success, STATUS_??? otherwise.
   */
  uint8_t MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, uint32_t data);
};

#endif