├── .gitignore ├── .vscode ├── arduino.json ├── c_cpp_properties.json └── settings.json ├── MFRC522.cpp ├── MFRC522.h ├── MFRC522Extended.cpp ├── MFRC522Extended.h ├── README.md ├── SpotifyClient.cpp ├── SpotifyClient.h ├── deprecated.h ├── esp32SpotifyEchoDot.ino ├── require_cpp11.h └── settings.h /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makeratplay/esp32SpotifyEchoDot/5a02a593315f04d813cdba18eca60950ca3a97d7/.gitignore -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "sketch": "esp32SpotifyEchoDot.ino", 3 | "board": "esp32:esp32:esp32", 4 | "configuration": "PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=none", 5 | "port": "COM6" 6 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceRoot}", 7 | "F:\\Users\\michael\\Dropbox\\Development\\MakerAtPlayGitHubRepo\\esp32SpotifyEchoDot\\**", 8 | "C:\\Users\\michael\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\**", 9 | "C:\\Users\\michael\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4\\**", 10 | "C:\\Users\\michael\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4\\cores\\**", 11 | "C:\\Users\\michael\\AppData\\Local\\Arduino15\\packages\\esp32\\hardware\\esp32\\1.0.4\\libraries\\**", 12 | "C:\\Users\\michael\\Documents\\Arduino\\libraries\\**", 13 | "C:\\Program Files (x86)\\Arduino\\libraries\\**" 14 | ], 15 | "forcedInclude": [], 16 | "defines": [ 17 | "USBCON" 18 | ], 19 | "intelliSenseMode": "msvc-x64", 20 | "cStandard": "c17", 21 | "cppStandard": "c++17" 22 | } 23 | ], 24 | "version": 4 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.h": "objective-cpp", 4 | "array": "cpp", 5 | "deque": "cpp", 6 | "list": "cpp", 7 | "string": "cpp", 8 | "unordered_map": "cpp", 9 | "unordered_set": "cpp", 10 | "vector": "cpp", 11 | "initializer_list": "cpp", 12 | "*.tcc": "cpp" 13 | } 14 | } -------------------------------------------------------------------------------- /MFRC522.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. 3 | * NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. 4 | * Released into the public domain. 5 | */ 6 | 7 | #include 8 | #include "MFRC522.h" 9 | 10 | ///////////////////////////////////////////////////////////////////////////////////// 11 | // Functions for setting up the Arduino 12 | ///////////////////////////////////////////////////////////////////////////////////// 13 | /** 14 | * Constructor. 15 | */ 16 | MFRC522::MFRC522(): MFRC522(SS, UINT8_MAX) { // SS is defined in pins_arduino.h, UINT8_MAX means there is no connection from Arduino to MFRC522's reset and power down input 17 | } // End constructor 18 | 19 | /** 20 | * Constructor. 21 | * Prepares the output pins. 22 | */ 23 | MFRC522::MFRC522( byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). 24 | ): MFRC522(SS, resetPowerDownPin) { // SS is defined in pins_arduino.h 25 | } // End constructor 26 | 27 | /** 28 | * Constructor. 29 | * Prepares the output pins. 30 | */ 31 | MFRC522::MFRC522( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) 32 | byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). 33 | ) { 34 | _chipSelectPin = chipSelectPin; 35 | _resetPowerDownPin = resetPowerDownPin; 36 | } // End constructor 37 | 38 | ///////////////////////////////////////////////////////////////////////////////////// 39 | // Basic interface functions for communicating with the MFRC522 40 | ///////////////////////////////////////////////////////////////////////////////////// 41 | 42 | /** 43 | * Writes a byte to the specified register in the MFRC522 chip. 44 | * The interface is described in the datasheet section 8.1.2. 45 | */ 46 | void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. 47 | byte value ///< The value to write. 48 | ) { 49 | SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus 50 | digitalWrite(_chipSelectPin, LOW); // Select slave 51 | SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. 52 | SPI.transfer(value); 53 | digitalWrite(_chipSelectPin, HIGH); // Release slave again 54 | SPI.endTransaction(); // Stop using the SPI bus 55 | } // End PCD_WriteRegister() 56 | 57 | /** 58 | * Writes a number of bytes to the specified register in the MFRC522 chip. 59 | * The interface is described in the datasheet section 8.1.2. 60 | */ 61 | void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. 62 | byte count, ///< The number of bytes to write to the register 63 | byte *values ///< The values to write. Byte array. 64 | ) { 65 | SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus 66 | digitalWrite(_chipSelectPin, LOW); // Select slave 67 | SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. 68 | for (byte index = 0; index < count; index++) { 69 | SPI.transfer(values[index]); 70 | } 71 | digitalWrite(_chipSelectPin, HIGH); // Release slave again 72 | SPI.endTransaction(); // Stop using the SPI bus 73 | } // End PCD_WriteRegister() 74 | 75 | /** 76 | * Reads a byte from the specified register in the MFRC522 chip. 77 | * The interface is described in the datasheet section 8.1.2. 78 | */ 79 | byte MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. 80 | ) { 81 | byte value; 82 | SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus 83 | digitalWrite(_chipSelectPin, LOW); // Select slave 84 | SPI.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. 85 | value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. 86 | digitalWrite(_chipSelectPin, HIGH); // Release slave again 87 | SPI.endTransaction(); // Stop using the SPI bus 88 | return value; 89 | } // End PCD_ReadRegister() 90 | 91 | /** 92 | * Reads a number of bytes from the specified register in the MFRC522 chip. 93 | * The interface is described in the datasheet section 8.1.2. 94 | */ 95 | void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from. One of the PCD_Register enums. 96 | byte count, ///< The number of bytes to read 97 | byte *values, ///< Byte array to store the values in. 98 | byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. 99 | ) { 100 | if (count == 0) { 101 | return; 102 | } 103 | //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); 104 | byte address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. 105 | byte index = 0; // Index in values array. 106 | SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus 107 | digitalWrite(_chipSelectPin, LOW); // Select slave 108 | count--; // One read is performed outside of the loop 109 | SPI.transfer(address); // Tell MFRC522 which address we want to read 110 | if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] 111 | // Create bit mask for bit positions rxAlign..7 112 | byte mask = (0xFF << rxAlign) & 0xFF; 113 | // Read value and tell that we want to read the same address again. 114 | byte value = SPI.transfer(address); 115 | // Apply mask to both current value of values[0] and the new data in value. 116 | values[0] = (values[0] & ~mask) | (value & mask); 117 | index++; 118 | } 119 | while (index < count) { 120 | values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. 121 | index++; 122 | } 123 | values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. 124 | digitalWrite(_chipSelectPin, HIGH); // Release slave again 125 | SPI.endTransaction(); // Stop using the SPI bus 126 | } // End PCD_ReadRegister() 127 | 128 | /** 129 | * Sets the bits given in mask in register reg. 130 | */ 131 | void MFRC522::PCD_SetRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. 132 | byte mask ///< The bits to set. 133 | ) { 134 | byte tmp; 135 | tmp = PCD_ReadRegister(reg); 136 | PCD_WriteRegister(reg, tmp | mask); // set bit mask 137 | } // End PCD_SetRegisterBitMask() 138 | 139 | /** 140 | * Clears the bits given in mask from register reg. 141 | */ 142 | void MFRC522::PCD_ClearRegisterBitMask( PCD_Register reg, ///< The register to update. One of the PCD_Register enums. 143 | byte mask ///< The bits to clear. 144 | ) { 145 | byte tmp; 146 | tmp = PCD_ReadRegister(reg); 147 | PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask 148 | } // End PCD_ClearRegisterBitMask() 149 | 150 | 151 | /** 152 | * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. 153 | * 154 | * @return STATUS_OK on success, STATUS_??? otherwise. 155 | */ 156 | MFRC522::StatusCode MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. 157 | byte length, ///< In: The number of bytes to transfer. 158 | byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. 159 | ) { 160 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. 161 | PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit 162 | PCD_WriteRegister(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization 163 | PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO 164 | PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation 165 | 166 | // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs. 167 | // TODO check/modify for other architectures than Arduino Uno 16bit 168 | 169 | // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us. 170 | for (uint16_t i = 5000; i > 0; i--) { 171 | // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved 172 | byte n = PCD_ReadRegister(DivIrqReg); 173 | if (n & 0x04) { // CRCIRq bit set - calculation done 174 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. 175 | // Transfer the result from the registers to the result buffer 176 | result[0] = PCD_ReadRegister(CRCResultRegL); 177 | result[1] = PCD_ReadRegister(CRCResultRegH); 178 | return STATUS_OK; 179 | } 180 | } 181 | // 89ms passed and nothing happend. Communication with the MFRC522 might be down. 182 | return STATUS_TIMEOUT; 183 | } // End PCD_CalculateCRC() 184 | 185 | 186 | ///////////////////////////////////////////////////////////////////////////////////// 187 | // Functions for manipulating the MFRC522 188 | ///////////////////////////////////////////////////////////////////////////////////// 189 | 190 | /** 191 | * Initializes the MFRC522 chip. 192 | */ 193 | void MFRC522::PCD_Init() { 194 | bool hardReset = false; 195 | 196 | // Set the chipSelectPin as digital output, do not select the slave yet 197 | pinMode(_chipSelectPin, OUTPUT); 198 | digitalWrite(_chipSelectPin, HIGH); 199 | 200 | // If a valid pin number has been set, pull device out of power down / reset state. 201 | if (_resetPowerDownPin != UNUSED_PIN) { 202 | // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. 203 | pinMode(_resetPowerDownPin, INPUT); 204 | 205 | if (digitalRead(_resetPowerDownPin) == LOW) { // The MFRC522 chip is in power down mode. 206 | pinMode(_resetPowerDownPin, OUTPUT); // Now set the resetPowerDownPin as digital output. 207 | digitalWrite(_resetPowerDownPin, LOW); // Make sure we have a clean LOW state. 208 | delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl 209 | digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. 210 | // 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. 211 | delay(50); 212 | hardReset = true; 213 | } 214 | } 215 | 216 | if (!hardReset) { // Perform a soft reset if we haven't triggered a hard reset above. 217 | PCD_Reset(); 218 | } 219 | 220 | // Reset baud rates 221 | PCD_WriteRegister(TxModeReg, 0x00); 222 | PCD_WriteRegister(RxModeReg, 0x00); 223 | // Reset ModWidthReg 224 | PCD_WriteRegister(ModWidthReg, 0x26); 225 | 226 | // When communicating with a PICC we need a timeout if something goes wrong. 227 | // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. 228 | // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. 229 | PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds 230 | PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. 231 | PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. 232 | PCD_WriteRegister(TReloadRegL, 0xE8); 233 | 234 | PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting 235 | PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) 236 | PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) 237 | } // End PCD_Init() 238 | 239 | /** 240 | * Initializes the MFRC522 chip. 241 | */ 242 | void MFRC522::PCD_Init( byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) 243 | ) { 244 | PCD_Init(SS, resetPowerDownPin); // SS is defined in pins_arduino.h 245 | } // End PCD_Init() 246 | 247 | /** 248 | * Initializes the MFRC522 chip. 249 | */ 250 | void MFRC522::PCD_Init( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) 251 | byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) 252 | ) { 253 | _chipSelectPin = chipSelectPin; 254 | _resetPowerDownPin = resetPowerDownPin; 255 | // Set the chipSelectPin as digital output, do not select the slave yet 256 | PCD_Init(); 257 | } // End PCD_Init() 258 | 259 | /** 260 | * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. 261 | */ 262 | void MFRC522::PCD_Reset() { 263 | PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. 264 | // The datasheet does not mention how long the SoftRest command takes to complete. 265 | // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) 266 | // 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. 267 | uint8_t count = 0; 268 | do { 269 | // Wait for the PowerDown bit in CommandReg to be cleared (max 3x50ms) 270 | delay(50); 271 | } while ((PCD_ReadRegister(CommandReg) & (1 << 4)) && (++count) < 3); 272 | } // End PCD_Reset() 273 | 274 | /** 275 | * Turns the antenna on by enabling pins TX1 and TX2. 276 | * After a reset these pins are disabled. 277 | */ 278 | void MFRC522::PCD_AntennaOn() { 279 | byte value = PCD_ReadRegister(TxControlReg); 280 | if ((value & 0x03) != 0x03) { 281 | PCD_WriteRegister(TxControlReg, value | 0x03); 282 | } 283 | } // End PCD_AntennaOn() 284 | 285 | /** 286 | * Turns the antenna off by disabling pins TX1 and TX2. 287 | */ 288 | void MFRC522::PCD_AntennaOff() { 289 | PCD_ClearRegisterBitMask(TxControlReg, 0x03); 290 | } // End PCD_AntennaOff() 291 | 292 | /** 293 | * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. 294 | * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 295 | * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. 296 | * 297 | * @return Value of the RxGain, scrubbed to the 3 bits used. 298 | */ 299 | byte MFRC522::PCD_GetAntennaGain() { 300 | return PCD_ReadRegister(RFCfgReg) & (0x07<<4); 301 | } // End PCD_GetAntennaGain() 302 | 303 | /** 304 | * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. 305 | * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 306 | * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. 307 | */ 308 | void MFRC522::PCD_SetAntennaGain(byte mask) { 309 | if (PCD_GetAntennaGain() != mask) { // only bother if there is a change 310 | PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern 311 | PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits 312 | } 313 | } // End PCD_SetAntennaGain() 314 | 315 | /** 316 | * Performs a self-test of the MFRC522 317 | * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf 318 | * 319 | * @return Whether or not the test passed. Or false if no firmware reference is available. 320 | */ 321 | bool MFRC522::PCD_PerformSelfTest() { 322 | // This follows directly the steps outlined in 16.1.1 323 | // 1. Perform a soft reset. 324 | PCD_Reset(); 325 | 326 | // 2. Clear the internal buffer by writing 25 bytes of 00h 327 | byte ZEROES[25] = {0x00}; 328 | PCD_WriteRegister(FIFOLevelReg, 0x80); // flush the FIFO buffer 329 | PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO 330 | PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer 331 | 332 | // 3. Enable self-test 333 | PCD_WriteRegister(AutoTestReg, 0x09); 334 | 335 | // 4. Write 00h to FIFO buffer 336 | PCD_WriteRegister(FIFODataReg, 0x00); 337 | 338 | // 5. Start self-test by issuing the CalcCRC command 339 | PCD_WriteRegister(CommandReg, PCD_CalcCRC); 340 | 341 | // 6. Wait for self-test to complete 342 | byte n; 343 | for (uint8_t i = 0; i < 0xFF; i++) { 344 | // The datasheet does not specify exact completion condition except 345 | // that FIFO buffer should contain 64 bytes. 346 | // While selftest is initiated by CalcCRC command 347 | // it behaves differently from normal CRC computation, 348 | // so one can't reliably use DivIrqReg to check for completion. 349 | // It is reported that some devices does not trigger CRCIRq flag 350 | // during selftest. 351 | n = PCD_ReadRegister(FIFOLevelReg); 352 | if (n >= 64) { 353 | break; 354 | } 355 | } 356 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. 357 | 358 | // 7. Read out resulting 64 bytes from the FIFO buffer. 359 | byte result[64]; 360 | PCD_ReadRegister(FIFODataReg, 64, result, 0); 361 | 362 | // Auto self-test done 363 | // Reset AutoTestReg register to be 0 again. Required for normal operation. 364 | PCD_WriteRegister(AutoTestReg, 0x00); 365 | 366 | // Determine firmware version (see section 9.3.4.8 in spec) 367 | byte version = PCD_ReadRegister(VersionReg); 368 | 369 | // Pick the appropriate reference values 370 | const byte *reference; 371 | switch (version) { 372 | case 0x88: // Fudan Semiconductor FM17522 clone 373 | reference = FM17522_firmware_reference; 374 | break; 375 | case 0x90: // Version 0.0 376 | reference = MFRC522_firmware_referenceV0_0; 377 | break; 378 | case 0x91: // Version 1.0 379 | reference = MFRC522_firmware_referenceV1_0; 380 | break; 381 | case 0x92: // Version 2.0 382 | reference = MFRC522_firmware_referenceV2_0; 383 | break; 384 | default: // Unknown version 385 | return false; // abort test 386 | } 387 | 388 | // Verify that the results match up to our expectations 389 | for (uint8_t i = 0; i < 64; i++) { 390 | if (result[i] != pgm_read_byte(&(reference[i]))) { 391 | return false; 392 | } 393 | } 394 | 395 | // Test passed; all is good. 396 | return true; 397 | } // End PCD_PerformSelfTest() 398 | 399 | ///////////////////////////////////////////////////////////////////////////////////// 400 | // Power control 401 | ///////////////////////////////////////////////////////////////////////////////////// 402 | 403 | //IMPORTANT NOTE!!!! 404 | //Calling any other function that uses CommandReg will disable soft power down mode !!! 405 | //For more details about power control, refer to the datasheet - page 33 (8.6) 406 | 407 | void MFRC522::PCD_SoftPowerDown(){//Note : Only soft power down mode is available throught software 408 | byte val = PCD_ReadRegister(CommandReg); // Read state of the command register 409 | val |= (1<<4);// set PowerDown bit ( bit 4 ) to 1 410 | PCD_WriteRegister(CommandReg, val);//write new value to the command register 411 | } 412 | 413 | void MFRC522::PCD_SoftPowerUp(){ 414 | byte val = PCD_ReadRegister(CommandReg); // Read state of the command register 415 | val &= ~(1<<4);// set PowerDown bit ( bit 4 ) to 0 416 | PCD_WriteRegister(CommandReg, val);//write new value to the command register 417 | // wait until PowerDown bit is cleared (this indicates end of wake up procedure) 418 | const uint32_t timeout = (uint32_t)millis() + 500;// create timer for timeout (just in case) 419 | 420 | while(millis()<=timeout){ // set timeout to 500 ms 421 | val = PCD_ReadRegister(CommandReg);// Read state of the command register 422 | if(!(val & (1<<4))){ // if powerdown bit is 0 423 | break;// wake up procedure is finished 424 | } 425 | } 426 | } 427 | 428 | ///////////////////////////////////////////////////////////////////////////////////// 429 | // Functions for communicating with PICCs 430 | ///////////////////////////////////////////////////////////////////////////////////// 431 | 432 | /** 433 | * Executes the Transceive command. 434 | * CRC validation can only be done if backData and backLen are specified. 435 | * 436 | * @return STATUS_OK on success, STATUS_??? otherwise. 437 | */ 438 | MFRC522::StatusCode MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. 439 | byte sendLen, ///< Number of bytes to transfer to the FIFO. 440 | byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. 441 | byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. 442 | byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default nullptr. 443 | byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. 444 | bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. 445 | ) { 446 | byte waitIRq = 0x30; // RxIRq and IdleIRq 447 | return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); 448 | } // End PCD_TransceiveData() 449 | 450 | /** 451 | * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. 452 | * CRC validation can only be done if backData and backLen are specified. 453 | * 454 | * @return STATUS_OK on success, STATUS_??? otherwise. 455 | */ 456 | MFRC522::StatusCode MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. 457 | byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. 458 | byte *sendData, ///< Pointer to the data to transfer to the FIFO. 459 | byte sendLen, ///< Number of bytes to transfer to the FIFO. 460 | byte *backData, ///< nullptr or pointer to buffer if data should be read back after executing the command. 461 | byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. 462 | byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. 463 | byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. 464 | bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. 465 | ) { 466 | // Prepare values for BitFramingReg 467 | byte txLastBits = validBits ? *validBits : 0; 468 | byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] 469 | 470 | PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. 471 | PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits 472 | PCD_WriteRegister(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization 473 | PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO 474 | PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments 475 | PCD_WriteRegister(CommandReg, command); // Execute the command 476 | if (command == PCD_Transceive) { 477 | PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts 478 | } 479 | 480 | // Wait for the command to complete. 481 | // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. 482 | // Each iteration of the do-while-loop takes 17.86μs. 483 | // TODO check/modify for other architectures than Arduino Uno 16bit 484 | uint16_t i; 485 | for (i = 2000; i > 0; i--) { 486 | byte n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq 487 | if (n & waitIRq) { // One of the interrupts that signal success has been set. 488 | break; 489 | } 490 | if (n & 0x01) { // Timer interrupt - nothing received in 25ms 491 | return STATUS_TIMEOUT; 492 | } 493 | } 494 | // 35.7ms and nothing happend. Communication with the MFRC522 might be down. 495 | if (i == 0) { 496 | return STATUS_TIMEOUT; 497 | } 498 | 499 | // Stop now if any errors except collisions were detected. 500 | byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr 501 | if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr 502 | return STATUS_ERROR; 503 | } 504 | 505 | byte _validBits = 0; 506 | 507 | // If the caller wants data back, get it from the MFRC522. 508 | if (backData && backLen) { 509 | byte n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO 510 | if (n > *backLen) { 511 | return STATUS_NO_ROOM; 512 | } 513 | *backLen = n; // Number of bytes returned 514 | PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO 515 | _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. 516 | if (validBits) { 517 | *validBits = _validBits; 518 | } 519 | } 520 | 521 | // Tell about collisions 522 | if (errorRegValue & 0x08) { // CollErr 523 | return STATUS_COLLISION; 524 | } 525 | 526 | // Perform CRC_A validation if requested. 527 | if (backData && backLen && checkCRC) { 528 | // In this case a MIFARE Classic NAK is not OK. 529 | if (*backLen == 1 && _validBits == 4) { 530 | return STATUS_MIFARE_NACK; 531 | } 532 | // We need at least the CRC_A value and all 8 bits of the last byte must be received. 533 | if (*backLen < 2 || _validBits != 0) { 534 | return STATUS_CRC_WRONG; 535 | } 536 | // Verify CRC_A - do our own calculation and store the control in controlBuffer. 537 | byte controlBuffer[2]; 538 | MFRC522::StatusCode status = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); 539 | if (status != STATUS_OK) { 540 | return status; 541 | } 542 | if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { 543 | return STATUS_CRC_WRONG; 544 | } 545 | } 546 | 547 | return STATUS_OK; 548 | } // End PCD_CommunicateWithPICC() 549 | 550 | /** 551 | * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. 552 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 553 | * 554 | * @return STATUS_OK on success, STATUS_??? otherwise. 555 | */ 556 | MFRC522::StatusCode MFRC522::PICC_RequestA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 557 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 558 | ) { 559 | return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); 560 | } // End PICC_RequestA() 561 | 562 | /** 563 | * 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. 564 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 565 | * 566 | * @return STATUS_OK on success, STATUS_??? otherwise. 567 | */ 568 | MFRC522::StatusCode MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 569 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 570 | ) { 571 | return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); 572 | } // End PICC_WakeupA() 573 | 574 | /** 575 | * Transmits REQA or WUPA commands. 576 | * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. 577 | * 578 | * @return STATUS_OK on success, STATUS_??? otherwise. 579 | */ 580 | MFRC522::StatusCode MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA 581 | byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in 582 | byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. 583 | ) { 584 | byte validBits; 585 | MFRC522::StatusCode status; 586 | 587 | if (bufferATQA == nullptr || *bufferSize < 2) { // The ATQA response is 2 bytes long. 588 | return STATUS_NO_ROOM; 589 | } 590 | PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. 591 | validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] 592 | status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); 593 | if (status != STATUS_OK) { 594 | return status; 595 | } 596 | if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. 597 | return STATUS_ERROR; 598 | } 599 | return STATUS_OK; 600 | } // End PICC_REQA_or_WUPA() 601 | 602 | /** 603 | * Transmits SELECT/ANTICOLLISION commands to select a single PICC. 604 | * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). 605 | * On success: 606 | * - 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.) 607 | * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. 608 | * 609 | * A PICC UID consists of 4, 7 or 10 bytes. 610 | * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: 611 | * UID size Number of UID bytes Cascade levels Example of PICC 612 | * ======== =================== ============== =============== 613 | * single 4 1 MIFARE Classic 614 | * double 7 2 MIFARE Ultralight 615 | * triple 10 3 Not currently in use? 616 | * 617 | * @return STATUS_OK on success, STATUS_??? otherwise. 618 | */ 619 | MFRC522::StatusCode MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. 620 | byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. 621 | ) { 622 | bool uidComplete; 623 | bool selectDone; 624 | bool useCascadeTag; 625 | byte cascadeLevel = 1; 626 | MFRC522::StatusCode result; 627 | byte count; 628 | byte checkBit; 629 | byte index; 630 | byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. 631 | int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. 632 | byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A 633 | byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. 634 | byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. 635 | byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. 636 | byte *responseBuffer; 637 | byte responseLength; 638 | 639 | // Description of buffer structure: 640 | // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 641 | // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. 642 | // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. 643 | // Byte 3: UID-data 644 | // Byte 4: UID-data 645 | // Byte 5: UID-data 646 | // Byte 6: BCC Block Check Character - XOR of bytes 2-5 647 | // Byte 7: CRC_A 648 | // Byte 8: CRC_A 649 | // The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level. 650 | // 651 | // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) 652 | // UID size Cascade level Byte2 Byte3 Byte4 Byte5 653 | // ======== ============= ===== ===== ===== ===== 654 | // 4 bytes 1 uid0 uid1 uid2 uid3 655 | // 7 bytes 1 CT uid0 uid1 uid2 656 | // 2 uid3 uid4 uid5 uid6 657 | // 10 bytes 1 CT uid0 uid1 uid2 658 | // 2 CT uid3 uid4 uid5 659 | // 3 uid6 uid7 uid8 uid9 660 | 661 | // Sanity checks 662 | if (validBits > 80) { 663 | return STATUS_INVALID; 664 | } 665 | 666 | // Prepare MFRC522 667 | PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. 668 | 669 | // Repeat Cascade Level loop until we have a complete UID. 670 | uidComplete = false; 671 | while (!uidComplete) { 672 | // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. 673 | switch (cascadeLevel) { 674 | case 1: 675 | buffer[0] = PICC_CMD_SEL_CL1; 676 | uidIndex = 0; 677 | useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes 678 | break; 679 | 680 | case 2: 681 | buffer[0] = PICC_CMD_SEL_CL2; 682 | uidIndex = 3; 683 | useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes 684 | break; 685 | 686 | case 3: 687 | buffer[0] = PICC_CMD_SEL_CL3; 688 | uidIndex = 6; 689 | useCascadeTag = false; // Never used in CL3. 690 | break; 691 | 692 | default: 693 | return STATUS_INTERNAL_ERROR; 694 | break; 695 | } 696 | 697 | // How many UID bits are known in this Cascade Level? 698 | currentLevelKnownBits = validBits - (8 * uidIndex); 699 | if (currentLevelKnownBits < 0) { 700 | currentLevelKnownBits = 0; 701 | } 702 | // Copy the known bits from uid->uidByte[] to buffer[] 703 | index = 2; // destination index in buffer[] 704 | if (useCascadeTag) { 705 | buffer[index++] = PICC_CMD_CT; 706 | } 707 | byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. 708 | if (bytesToCopy) { 709 | byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag 710 | if (bytesToCopy > maxBytes) { 711 | bytesToCopy = maxBytes; 712 | } 713 | for (count = 0; count < bytesToCopy; count++) { 714 | buffer[index++] = uid->uidByte[uidIndex + count]; 715 | } 716 | } 717 | // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits 718 | if (useCascadeTag) { 719 | currentLevelKnownBits += 8; 720 | } 721 | 722 | // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. 723 | selectDone = false; 724 | while (!selectDone) { 725 | // Find out how many bits and bytes to send and receive. 726 | if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. 727 | //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 728 | buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes 729 | // Calculate BCC - Block Check Character 730 | buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; 731 | // Calculate CRC_A 732 | result = PCD_CalculateCRC(buffer, 7, &buffer[7]); 733 | if (result != STATUS_OK) { 734 | return result; 735 | } 736 | txLastBits = 0; // 0 => All 8 bits are valid. 737 | bufferUsed = 9; 738 | // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) 739 | responseBuffer = &buffer[6]; 740 | responseLength = 3; 741 | } 742 | else { // This is an ANTICOLLISION. 743 | //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 744 | txLastBits = currentLevelKnownBits % 8; 745 | count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. 746 | index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs 747 | buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits 748 | bufferUsed = index + (txLastBits ? 1 : 0); 749 | // Store response in the unused part of buffer 750 | responseBuffer = &buffer[index]; 751 | responseLength = sizeof(buffer) - index; 752 | } 753 | 754 | // Set bit adjustments 755 | rxAlign = txLastBits; // Having a separate variable is overkill. But it makes the next line easier to read. 756 | PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] 757 | 758 | // Transmit the buffer and receive the response. 759 | result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); 760 | if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. 761 | byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] 762 | if (valueOfCollReg & 0x20) { // CollPosNotValid 763 | return STATUS_COLLISION; // Without a valid collision position we cannot continue 764 | } 765 | byte collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32. 766 | if (collisionPos == 0) { 767 | collisionPos = 32; 768 | } 769 | if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen 770 | return STATUS_INTERNAL_ERROR; 771 | } 772 | // Choose the PICC with the bit set. 773 | currentLevelKnownBits = collisionPos; 774 | count = currentLevelKnownBits % 8; // The bit to modify 775 | checkBit = (currentLevelKnownBits - 1) % 8; 776 | index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. 777 | buffer[index] |= (1 << checkBit); 778 | } 779 | else if (result != STATUS_OK) { 780 | return result; 781 | } 782 | else { // STATUS_OK 783 | if (currentLevelKnownBits >= 32) { // This was a SELECT. 784 | selectDone = true; // No more anticollision 785 | // We continue below outside the while. 786 | } 787 | else { // This was an ANTICOLLISION. 788 | // We now have all 32 bits of the UID in this Cascade Level 789 | currentLevelKnownBits = 32; 790 | // Run loop again to do the SELECT. 791 | } 792 | } 793 | } // End of while (!selectDone) 794 | 795 | // We do not check the CBB - it was constructed by us above. 796 | 797 | // Copy the found UID bytes from buffer[] to uid->uidByte[] 798 | index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] 799 | bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; 800 | for (count = 0; count < bytesToCopy; count++) { 801 | uid->uidByte[uidIndex + count] = buffer[index++]; 802 | } 803 | 804 | // Check response SAK (Select Acknowledge) 805 | if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). 806 | return STATUS_ERROR; 807 | } 808 | // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. 809 | result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); 810 | if (result != STATUS_OK) { 811 | return result; 812 | } 813 | if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { 814 | return STATUS_CRC_WRONG; 815 | } 816 | if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes 817 | cascadeLevel++; 818 | } 819 | else { 820 | uidComplete = true; 821 | uid->sak = responseBuffer[0]; 822 | } 823 | } // End of while (!uidComplete) 824 | 825 | // Set correct uid->size 826 | uid->size = 3 * cascadeLevel + 1; 827 | 828 | return STATUS_OK; 829 | } // End PICC_Select() 830 | 831 | /** 832 | * Instructs a PICC in state ACTIVE(*) to go to state HALT. 833 | * 834 | * @return STATUS_OK on success, STATUS_??? otherwise. 835 | */ 836 | MFRC522::StatusCode MFRC522::PICC_HaltA() { 837 | MFRC522::StatusCode result; 838 | byte buffer[4]; 839 | 840 | // Build command buffer 841 | buffer[0] = PICC_CMD_HLTA; 842 | buffer[1] = 0; 843 | // Calculate CRC_A 844 | result = PCD_CalculateCRC(buffer, 2, &buffer[2]); 845 | if (result != STATUS_OK) { 846 | return result; 847 | } 848 | 849 | // Send the command. 850 | // The standard says: 851 | // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the 852 | // HLTA command, this response shall be interpreted as 'not acknowledge'. 853 | // We interpret that this way: Only STATUS_TIMEOUT is a success. 854 | result = PCD_TransceiveData(buffer, sizeof(buffer), nullptr, 0); 855 | if (result == STATUS_TIMEOUT) { 856 | return STATUS_OK; 857 | } 858 | if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) 859 | return STATUS_ERROR; 860 | } 861 | return result; 862 | } // End PICC_HaltA() 863 | 864 | ///////////////////////////////////////////////////////////////////////////////////// 865 | // Functions for communicating with MIFARE PICCs 866 | ///////////////////////////////////////////////////////////////////////////////////// 867 | 868 | /** 869 | * Executes the MFRC522 MFAuthent command. 870 | * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. 871 | * 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. 872 | * For use with MIFARE Classic PICCs. 873 | * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. 874 | * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. 875 | * 876 | * All keys are set to FFFFFFFFFFFFh at chip delivery. 877 | * 878 | * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. 879 | */ 880 | MFRC522::StatusCode MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B 881 | byte blockAddr, ///< The block number. See numbering in the comments in the .h file. 882 | MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) 883 | Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. 884 | ) { 885 | byte waitIRq = 0x10; // IdleIRq 886 | 887 | // Build command buffer 888 | byte sendData[12]; 889 | sendData[0] = command; 890 | sendData[1] = blockAddr; 891 | for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes 892 | sendData[2+i] = key->keyByte[i]; 893 | } 894 | // Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf 895 | // section 3.2.5 "MIFARE Classic Authentication". 896 | // The only missed case is the MF1Sxxxx shortcut activation, 897 | // but it requires cascade tag (CT) byte, that is not part of uid. 898 | for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID 899 | sendData[8+i] = uid->uidByte[i+uid->size-4]; 900 | } 901 | 902 | // Start the authentication. 903 | return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); 904 | } // End PCD_Authenticate() 905 | 906 | /** 907 | * Used to exit the PCD from its authenticated state. 908 | * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. 909 | */ 910 | void MFRC522::PCD_StopCrypto1() { 911 | // Clear MFCrypto1On bit 912 | PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] 913 | } // End PCD_StopCrypto1() 914 | 915 | /** 916 | * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. 917 | * 918 | * For MIFARE Classic the sector containing the block must be authenticated before calling this function. 919 | * 920 | * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. 921 | * The MF0ICU1 returns a NAK for higher addresses. 922 | * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. 923 | * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. 924 | * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. 925 | * 926 | * The buffer must be at least 18 bytes because a CRC_A is also returned. 927 | * Checks the CRC_A before returning STATUS_OK. 928 | * 929 | * @return STATUS_OK on success, STATUS_??? otherwise. 930 | */ 931 | MFRC522::StatusCode MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. 932 | byte *buffer, ///< The buffer to store the data in 933 | byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. 934 | ) { 935 | MFRC522::StatusCode result; 936 | 937 | // Sanity check 938 | if (buffer == nullptr || *bufferSize < 18) { 939 | return STATUS_NO_ROOM; 940 | } 941 | 942 | // Build command buffer 943 | buffer[0] = PICC_CMD_MF_READ; 944 | buffer[1] = blockAddr; 945 | // Calculate CRC_A 946 | result = PCD_CalculateCRC(buffer, 2, &buffer[2]); 947 | if (result != STATUS_OK) { 948 | return result; 949 | } 950 | 951 | // Transmit the buffer and receive the response, validate CRC_A. 952 | return PCD_TransceiveData(buffer, 4, buffer, bufferSize, nullptr, 0, true); 953 | } // End MIFARE_Read() 954 | 955 | /** 956 | * Writes 16 bytes to the active PICC. 957 | * 958 | * For MIFARE Classic the sector containing the block must be authenticated before calling this function. 959 | * 960 | * For MIFARE Ultralight the operation is called "COMPATIBILITY WRITE". 961 | * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) 962 | * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. 963 | * * 964 | * @return STATUS_OK on success, STATUS_??? otherwise. 965 | */ 966 | MFRC522::StatusCode MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. 967 | byte *buffer, ///< The 16 bytes to write to the PICC 968 | byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. 969 | ) { 970 | MFRC522::StatusCode result; 971 | 972 | // Sanity check 973 | if (buffer == nullptr || bufferSize < 16) { 974 | return STATUS_INVALID; 975 | } 976 | 977 | // Mifare Classic protocol requires two communications to perform a write. 978 | // Step 1: Tell the PICC we want to write to block blockAddr. 979 | byte cmdBuffer[2]; 980 | cmdBuffer[0] = PICC_CMD_MF_WRITE; 981 | cmdBuffer[1] = blockAddr; 982 | result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 983 | if (result != STATUS_OK) { 984 | return result; 985 | } 986 | 987 | // Step 2: Transfer the data 988 | result = PCD_MIFARE_Transceive(buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. 989 | if (result != STATUS_OK) { 990 | return result; 991 | } 992 | 993 | return STATUS_OK; 994 | } // End MIFARE_Write() 995 | 996 | /** 997 | * Writes a 4 byte page to the active MIFARE Ultralight PICC. 998 | * 999 | * @return STATUS_OK on success, STATUS_??? otherwise. 1000 | */ 1001 | MFRC522::StatusCode MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. 1002 | byte *buffer, ///< The 4 bytes to write to the PICC 1003 | byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. 1004 | ) { 1005 | MFRC522::StatusCode result; 1006 | 1007 | // Sanity check 1008 | if (buffer == nullptr || bufferSize < 4) { 1009 | return STATUS_INVALID; 1010 | } 1011 | 1012 | // Build commmand buffer 1013 | byte cmdBuffer[6]; 1014 | cmdBuffer[0] = PICC_CMD_UL_WRITE; 1015 | cmdBuffer[1] = page; 1016 | memcpy(&cmdBuffer[2], buffer, 4); 1017 | 1018 | // Perform the write 1019 | result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. 1020 | if (result != STATUS_OK) { 1021 | return result; 1022 | } 1023 | return STATUS_OK; 1024 | } // End MIFARE_Ultralight_Write() 1025 | 1026 | /** 1027 | * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. 1028 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 1029 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 1030 | * Use MIFARE_Transfer() to store the result in a block. 1031 | * 1032 | * @return STATUS_OK on success, STATUS_??? otherwise. 1033 | */ 1034 | MFRC522::StatusCode MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. 1035 | int32_t delta ///< This number is subtracted from the value of block blockAddr. 1036 | ) { 1037 | return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); 1038 | } // End MIFARE_Decrement() 1039 | 1040 | /** 1041 | * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. 1042 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 1043 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 1044 | * Use MIFARE_Transfer() to store the result in a block. 1045 | * 1046 | * @return STATUS_OK on success, STATUS_??? otherwise. 1047 | */ 1048 | MFRC522::StatusCode MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. 1049 | int32_t delta ///< This number is added to the value of block blockAddr. 1050 | ) { 1051 | return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); 1052 | } // End MIFARE_Increment() 1053 | 1054 | /** 1055 | * MIFARE Restore copies the value of the addressed block into a volatile memory. 1056 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 1057 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 1058 | * Use MIFARE_Transfer() to store the result in a block. 1059 | * 1060 | * @return STATUS_OK on success, STATUS_??? otherwise. 1061 | */ 1062 | MFRC522::StatusCode MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. 1063 | ) { 1064 | // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. 1065 | // Doing only a single step does not work, so I chose to transfer 0L in step two. 1066 | return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); 1067 | } // End MIFARE_Restore() 1068 | 1069 | /** 1070 | * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. 1071 | * 1072 | * @return STATUS_OK on success, STATUS_??? otherwise. 1073 | */ 1074 | MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use 1075 | byte blockAddr, ///< The block (0-0xff) number. 1076 | int32_t data ///< The data to transfer in step 2 1077 | ) { 1078 | MFRC522::StatusCode result; 1079 | byte cmdBuffer[2]; // We only need room for 2 bytes. 1080 | 1081 | // Step 1: Tell the PICC the command and block address 1082 | cmdBuffer[0] = command; 1083 | cmdBuffer[1] = blockAddr; 1084 | result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 1085 | if (result != STATUS_OK) { 1086 | return result; 1087 | } 1088 | 1089 | // Step 2: Transfer the data 1090 | result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. 1091 | if (result != STATUS_OK) { 1092 | return result; 1093 | } 1094 | 1095 | return STATUS_OK; 1096 | } // End MIFARE_TwoStepHelper() 1097 | 1098 | /** 1099 | * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. 1100 | * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. 1101 | * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. 1102 | * 1103 | * @return STATUS_OK on success, STATUS_??? otherwise. 1104 | */ 1105 | MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. 1106 | ) { 1107 | MFRC522::StatusCode result; 1108 | byte cmdBuffer[2]; // We only need room for 2 bytes. 1109 | 1110 | // Tell the PICC we want to transfer the result into block blockAddr. 1111 | cmdBuffer[0] = PICC_CMD_MF_TRANSFER; 1112 | cmdBuffer[1] = blockAddr; 1113 | result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. 1114 | if (result != STATUS_OK) { 1115 | return result; 1116 | } 1117 | return STATUS_OK; 1118 | } // End MIFARE_Transfer() 1119 | 1120 | /** 1121 | * Helper routine to read the current value from a Value Block. 1122 | * 1123 | * Only for MIFARE Classic and only for blocks in "value block" mode, that 1124 | * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing 1125 | * the block must be authenticated before calling this function. 1126 | * 1127 | * @param[in] blockAddr The block (0x00-0xff) number. 1128 | * @param[out] value Current value of the Value Block. 1129 | * @return STATUS_OK on success, STATUS_??? otherwise. 1130 | */ 1131 | MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { 1132 | MFRC522::StatusCode status; 1133 | byte buffer[18]; 1134 | byte size = sizeof(buffer); 1135 | 1136 | // Read the block 1137 | status = MIFARE_Read(blockAddr, buffer, &size); 1138 | if (status == STATUS_OK) { 1139 | // Extract the value 1140 | *value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); 1141 | } 1142 | return status; 1143 | } // End MIFARE_GetValue() 1144 | 1145 | /** 1146 | * Helper routine to write a specific value into a Value Block. 1147 | * 1148 | * Only for MIFARE Classic and only for blocks in "value block" mode, that 1149 | * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing 1150 | * the block must be authenticated before calling this function. 1151 | * 1152 | * @param[in] blockAddr The block (0x00-0xff) number. 1153 | * @param[in] value New value of the Value Block. 1154 | * @return STATUS_OK on success, STATUS_??? otherwise. 1155 | */ 1156 | MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { 1157 | byte buffer[18]; 1158 | 1159 | // Translate the int32_t into 4 bytes; repeated 2x in value block 1160 | buffer[0] = buffer[ 8] = (value & 0xFF); 1161 | buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; 1162 | buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; 1163 | buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; 1164 | // Inverse 4 bytes also found in value block 1165 | buffer[4] = ~buffer[0]; 1166 | buffer[5] = ~buffer[1]; 1167 | buffer[6] = ~buffer[2]; 1168 | buffer[7] = ~buffer[3]; 1169 | // Address 2x with inverse address 2x 1170 | buffer[12] = buffer[14] = blockAddr; 1171 | buffer[13] = buffer[15] = ~blockAddr; 1172 | 1173 | // Write the whole data block 1174 | return MIFARE_Write(blockAddr, buffer, 16); 1175 | } // End MIFARE_SetValue() 1176 | 1177 | /** 1178 | * Authenticate with a NTAG216. 1179 | * 1180 | * Only for NTAG216. First implemented by Gargantuanman. 1181 | * 1182 | * @param[in] passWord password. 1183 | * @param[in] pACK result success???. 1184 | * @return STATUS_OK on success, STATUS_??? otherwise. 1185 | */ 1186 | MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Authenticate with 32bit password 1187 | { 1188 | // TODO: Fix cmdBuffer length and rxlength. They really should match. 1189 | // (Better still, rxlength should not even be necessary.) 1190 | 1191 | MFRC522::StatusCode result; 1192 | byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. 1193 | 1194 | cmdBuffer[0] = 0x1B; //Comando de autentificacion 1195 | 1196 | for (byte i = 0; i<4; i++) 1197 | cmdBuffer[i+1] = passWord[i]; 1198 | 1199 | result = PCD_CalculateCRC(cmdBuffer, 5, &cmdBuffer[5]); 1200 | 1201 | if (result!=STATUS_OK) { 1202 | return result; 1203 | } 1204 | 1205 | // Transceive the data, store the reply in cmdBuffer[] 1206 | byte waitIRq = 0x30; // RxIRq and IdleIRq 1207 | // byte cmdBufferSize = sizeof(cmdBuffer); 1208 | byte validBits = 0; 1209 | byte rxlength = 5; 1210 | result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, 7, cmdBuffer, &rxlength, &validBits); 1211 | 1212 | pACK[0] = cmdBuffer[0]; 1213 | pACK[1] = cmdBuffer[1]; 1214 | 1215 | if (result!=STATUS_OK) { 1216 | return result; 1217 | } 1218 | 1219 | return STATUS_OK; 1220 | } // End PCD_NTAG216_AUTH() 1221 | 1222 | 1223 | ///////////////////////////////////////////////////////////////////////////////////// 1224 | // Support functions 1225 | ///////////////////////////////////////////////////////////////////////////////////// 1226 | 1227 | /** 1228 | * Wrapper for MIFARE protocol communication. 1229 | * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. 1230 | * 1231 | * @return STATUS_OK on success, STATUS_??? otherwise. 1232 | */ 1233 | MFRC522::StatusCode MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. 1234 | byte sendLen, ///< Number of bytes in sendData. 1235 | bool acceptTimeout ///< True => A timeout is also success 1236 | ) { 1237 | MFRC522::StatusCode result; 1238 | byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. 1239 | 1240 | // Sanity check 1241 | if (sendData == nullptr || sendLen > 16) { 1242 | return STATUS_INVALID; 1243 | } 1244 | 1245 | // Copy sendData[] to cmdBuffer[] and add CRC_A 1246 | memcpy(cmdBuffer, sendData, sendLen); 1247 | result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); 1248 | if (result != STATUS_OK) { 1249 | return result; 1250 | } 1251 | sendLen += 2; 1252 | 1253 | // Transceive the data, store the reply in cmdBuffer[] 1254 | byte waitIRq = 0x30; // RxIRq and IdleIRq 1255 | byte cmdBufferSize = sizeof(cmdBuffer); 1256 | byte validBits = 0; 1257 | result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); 1258 | if (acceptTimeout && result == STATUS_TIMEOUT) { 1259 | return STATUS_OK; 1260 | } 1261 | if (result != STATUS_OK) { 1262 | return result; 1263 | } 1264 | // The PICC must reply with a 4 bit ACK 1265 | if (cmdBufferSize != 1 || validBits != 4) { 1266 | return STATUS_ERROR; 1267 | } 1268 | if (cmdBuffer[0] != MF_ACK) { 1269 | return STATUS_MIFARE_NACK; 1270 | } 1271 | return STATUS_OK; 1272 | } // End PCD_MIFARE_Transceive() 1273 | 1274 | /** 1275 | * Returns a __FlashStringHelper pointer to a status code name. 1276 | * 1277 | * @return const __FlashStringHelper * 1278 | */ 1279 | const __FlashStringHelper *MFRC522::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums. 1280 | ) { 1281 | switch (code) { 1282 | case STATUS_OK: return F("Success."); 1283 | case STATUS_ERROR: return F("Error in communication."); 1284 | case STATUS_COLLISION: return F("Collision detected."); 1285 | case STATUS_TIMEOUT: return F("Timeout in communication."); 1286 | case STATUS_NO_ROOM: return F("A buffer is not big enough."); 1287 | case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); 1288 | case STATUS_INVALID: return F("Invalid argument."); 1289 | case STATUS_CRC_WRONG: return F("The CRC_A does not match."); 1290 | case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); 1291 | default: return F("Unknown error"); 1292 | } 1293 | } // End GetStatusCodeName() 1294 | 1295 | /** 1296 | * Translates the SAK (Select Acknowledge) to a PICC type. 1297 | * 1298 | * @return PICC_Type 1299 | */ 1300 | MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). 1301 | ) { 1302 | // http://www.nxp.com/documents/application_note/AN10833.pdf 1303 | // 3.2 Coding of Select Acknowledge (SAK) 1304 | // ignore 8-bit (iso14443 starts with LSBit = bit 1) 1305 | // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) 1306 | sak &= 0x7F; 1307 | switch (sak) { 1308 | case 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete 1309 | case 0x09: return PICC_TYPE_MIFARE_MINI; 1310 | case 0x08: return PICC_TYPE_MIFARE_1K; 1311 | case 0x18: return PICC_TYPE_MIFARE_4K; 1312 | case 0x00: return PICC_TYPE_MIFARE_UL; 1313 | case 0x10: 1314 | case 0x11: return PICC_TYPE_MIFARE_PLUS; 1315 | case 0x01: return PICC_TYPE_TNP3XXX; 1316 | case 0x20: return PICC_TYPE_ISO_14443_4; 1317 | case 0x40: return PICC_TYPE_ISO_18092; 1318 | default: return PICC_TYPE_UNKNOWN; 1319 | } 1320 | } // End PICC_GetType() 1321 | 1322 | /** 1323 | * Returns a __FlashStringHelper pointer to the PICC type name. 1324 | * 1325 | * @return const __FlashStringHelper * 1326 | */ 1327 | const __FlashStringHelper *MFRC522::PICC_GetTypeName(PICC_Type piccType ///< One of the PICC_Type enums. 1328 | ) { 1329 | switch (piccType) { 1330 | case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); 1331 | case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)"); 1332 | case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); 1333 | case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); 1334 | case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); 1335 | case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); 1336 | case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); 1337 | case PICC_TYPE_MIFARE_DESFIRE: return F("MIFARE DESFire"); 1338 | case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); 1339 | case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); 1340 | case PICC_TYPE_UNKNOWN: 1341 | default: return F("Unknown type"); 1342 | } 1343 | } // End PICC_GetTypeName() 1344 | 1345 | /** 1346 | * Dumps debug info about the connected PCD to Serial. 1347 | * Shows all known firmware versions 1348 | */ 1349 | void MFRC522::PCD_DumpVersionToSerial() { 1350 | // Get the MFRC522 firmware version 1351 | byte v = PCD_ReadRegister(VersionReg); 1352 | Serial.print(F("Firmware Version: 0x")); 1353 | Serial.print(v, HEX); 1354 | // Lookup which version 1355 | switch(v) { 1356 | case 0x88: Serial.println(F(" = (clone)")); break; 1357 | case 0x90: Serial.println(F(" = v0.0")); break; 1358 | case 0x91: Serial.println(F(" = v1.0")); break; 1359 | case 0x92: Serial.println(F(" = v2.0")); break; 1360 | case 0x12: Serial.println(F(" = counterfeit chip")); break; 1361 | default: Serial.println(F(" = (unknown)")); 1362 | } 1363 | // When 0x00 or 0xFF is returned, communication probably failed 1364 | if ((v == 0x00) || (v == 0xFF)) 1365 | Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?")); 1366 | } // End PCD_DumpVersionToSerial() 1367 | 1368 | /** 1369 | * Dumps debug info about the selected PICC to Serial. 1370 | * On success the PICC is halted after dumping the data. 1371 | * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. 1372 | */ 1373 | void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). 1374 | ) { 1375 | MIFARE_Key key; 1376 | 1377 | // Dump UID, SAK and Type 1378 | PICC_DumpDetailsToSerial(uid); 1379 | 1380 | // Dump contents 1381 | PICC_Type piccType = PICC_GetType(uid->sak); 1382 | switch (piccType) { 1383 | case PICC_TYPE_MIFARE_MINI: 1384 | case PICC_TYPE_MIFARE_1K: 1385 | case PICC_TYPE_MIFARE_4K: 1386 | // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. 1387 | for (byte i = 0; i < 6; i++) { 1388 | key.keyByte[i] = 0xFF; 1389 | } 1390 | PICC_DumpMifareClassicToSerial(uid, piccType, &key); 1391 | break; 1392 | 1393 | case PICC_TYPE_MIFARE_UL: 1394 | PICC_DumpMifareUltralightToSerial(); 1395 | break; 1396 | 1397 | case PICC_TYPE_ISO_14443_4: 1398 | case PICC_TYPE_MIFARE_DESFIRE: 1399 | case PICC_TYPE_ISO_18092: 1400 | case PICC_TYPE_MIFARE_PLUS: 1401 | case PICC_TYPE_TNP3XXX: 1402 | Serial.println(F("Dumping memory contents not implemented for that PICC type.")); 1403 | break; 1404 | 1405 | case PICC_TYPE_UNKNOWN: 1406 | case PICC_TYPE_NOT_COMPLETE: 1407 | default: 1408 | break; // No memory dump here 1409 | } 1410 | 1411 | Serial.println(); 1412 | PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. 1413 | } // End PICC_DumpToSerial() 1414 | 1415 | /** 1416 | * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. 1417 | */ 1418 | void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). 1419 | ) { 1420 | // UID 1421 | Serial.print(F("Card UID:")); 1422 | for (byte i = 0; i < uid->size; i++) { 1423 | if(uid->uidByte[i] < 0x10) 1424 | Serial.print(F(" 0")); 1425 | else 1426 | Serial.print(F(" ")); 1427 | Serial.print(uid->uidByte[i], HEX); 1428 | } 1429 | Serial.println(); 1430 | 1431 | // SAK 1432 | Serial.print(F("Card SAK: ")); 1433 | if(uid->sak < 0x10) 1434 | Serial.print(F("0")); 1435 | Serial.println(uid->sak, HEX); 1436 | 1437 | // (suggested) PICC type 1438 | PICC_Type piccType = PICC_GetType(uid->sak); 1439 | Serial.print(F("PICC type: ")); 1440 | Serial.println(PICC_GetTypeName(piccType)); 1441 | } // End PICC_DumpDetailsToSerial() 1442 | 1443 | /** 1444 | * Dumps memory contents of a MIFARE Classic PICC. 1445 | * On success the PICC is halted after dumping the data. 1446 | */ 1447 | void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). 1448 | PICC_Type piccType, ///< One of the PICC_Type enums. 1449 | MIFARE_Key *key ///< Key A used for all sectors. 1450 | ) { 1451 | byte no_of_sectors = 0; 1452 | switch (piccType) { 1453 | case PICC_TYPE_MIFARE_MINI: 1454 | // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. 1455 | no_of_sectors = 5; 1456 | break; 1457 | 1458 | case PICC_TYPE_MIFARE_1K: 1459 | // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. 1460 | no_of_sectors = 16; 1461 | break; 1462 | 1463 | case PICC_TYPE_MIFARE_4K: 1464 | // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. 1465 | no_of_sectors = 40; 1466 | break; 1467 | 1468 | default: // Should not happen. Ignore. 1469 | break; 1470 | } 1471 | 1472 | // Dump sectors, highest address first. 1473 | if (no_of_sectors) { 1474 | Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); 1475 | for (int8_t i = no_of_sectors - 1; i >= 0; i--) { 1476 | PICC_DumpMifareClassicSectorToSerial(uid, key, i); 1477 | } 1478 | } 1479 | PICC_HaltA(); // Halt the PICC before stopping the encrypted session. 1480 | PCD_StopCrypto1(); 1481 | } // End PICC_DumpMifareClassicToSerial() 1482 | 1483 | /** 1484 | * Dumps memory contents of a sector of a MIFARE Classic PICC. 1485 | * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. 1486 | * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. 1487 | */ 1488 | void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). 1489 | MIFARE_Key *key, ///< Key A for the sector. 1490 | byte sector ///< The sector to dump, 0..39. 1491 | ) { 1492 | MFRC522::StatusCode status; 1493 | byte firstBlock; // Address of lowest address to dump actually last block dumped) 1494 | byte no_of_blocks; // Number of blocks in sector 1495 | bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. 1496 | 1497 | // The access bits are stored in a peculiar fashion. 1498 | // There are four groups: 1499 | // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) 1500 | // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) 1501 | // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) 1502 | // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) 1503 | // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. 1504 | // The four CX bits are stored together in a nible cx and an inverted nible cx_. 1505 | byte c1, c2, c3; // Nibbles 1506 | byte c1_, c2_, c3_; // Inverted nibbles 1507 | bool invertedError; // True if one of the inverted nibbles did not match 1508 | byte g[4]; // Access bits for each of the four groups. 1509 | byte group; // 0-3 - active group for access bits 1510 | bool firstInGroup; // True for the first block dumped in the group 1511 | 1512 | // Determine position and size of sector. 1513 | if (sector < 32) { // Sectors 0..31 has 4 blocks each 1514 | no_of_blocks = 4; 1515 | firstBlock = sector * no_of_blocks; 1516 | } 1517 | else if (sector < 40) { // Sectors 32-39 has 16 blocks each 1518 | no_of_blocks = 16; 1519 | firstBlock = 128 + (sector - 32) * no_of_blocks; 1520 | } 1521 | else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. 1522 | return; 1523 | } 1524 | 1525 | // Dump blocks, highest address first. 1526 | byte byteCount; 1527 | byte buffer[18]; 1528 | byte blockAddr; 1529 | isSectorTrailer = true; 1530 | invertedError = false; // Avoid "unused variable" warning. 1531 | for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { 1532 | blockAddr = firstBlock + blockOffset; 1533 | // Sector number - only on first line 1534 | if (isSectorTrailer) { 1535 | if(sector < 10) 1536 | Serial.print(F(" ")); // Pad with spaces 1537 | else 1538 | Serial.print(F(" ")); // Pad with spaces 1539 | Serial.print(sector); 1540 | Serial.print(F(" ")); 1541 | } 1542 | else { 1543 | Serial.print(F(" ")); 1544 | } 1545 | // Block number 1546 | if(blockAddr < 10) 1547 | Serial.print(F(" ")); // Pad with spaces 1548 | else { 1549 | if(blockAddr < 100) 1550 | Serial.print(F(" ")); // Pad with spaces 1551 | else 1552 | Serial.print(F(" ")); // Pad with spaces 1553 | } 1554 | Serial.print(blockAddr); 1555 | Serial.print(F(" ")); 1556 | // Establish encrypted communications before reading the first block 1557 | if (isSectorTrailer) { 1558 | status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); 1559 | if (status != STATUS_OK) { 1560 | Serial.print(F("PCD_Authenticate() failed: ")); 1561 | Serial.println(GetStatusCodeName(status)); 1562 | return; 1563 | } 1564 | } 1565 | // Read block 1566 | byteCount = sizeof(buffer); 1567 | status = MIFARE_Read(blockAddr, buffer, &byteCount); 1568 | if (status != STATUS_OK) { 1569 | Serial.print(F("MIFARE_Read() failed: ")); 1570 | Serial.println(GetStatusCodeName(status)); 1571 | continue; 1572 | } 1573 | // Dump data 1574 | for (byte index = 0; index < 16; index++) { 1575 | if(buffer[index] < 0x10) 1576 | Serial.print(F(" 0")); 1577 | else 1578 | Serial.print(F(" ")); 1579 | Serial.print(buffer[index], HEX); 1580 | if ((index % 4) == 3) { 1581 | Serial.print(F(" ")); 1582 | } 1583 | } 1584 | // Parse sector trailer data 1585 | if (isSectorTrailer) { 1586 | c1 = buffer[7] >> 4; 1587 | c2 = buffer[8] & 0xF; 1588 | c3 = buffer[8] >> 4; 1589 | c1_ = buffer[6] & 0xF; 1590 | c2_ = buffer[6] >> 4; 1591 | c3_ = buffer[7] & 0xF; 1592 | invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); 1593 | g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); 1594 | g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); 1595 | g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); 1596 | g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); 1597 | isSectorTrailer = false; 1598 | } 1599 | 1600 | // Which access group is this block in? 1601 | if (no_of_blocks == 4) { 1602 | group = blockOffset; 1603 | firstInGroup = true; 1604 | } 1605 | else { 1606 | group = blockOffset / 5; 1607 | firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); 1608 | } 1609 | 1610 | if (firstInGroup) { 1611 | // Print access bits 1612 | Serial.print(F(" [ ")); 1613 | Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); 1614 | Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); 1615 | Serial.print((g[group] >> 0) & 1, DEC); 1616 | Serial.print(F(" ] ")); 1617 | if (invertedError) { 1618 | Serial.print(F(" Inverted access bits did not match! ")); 1619 | } 1620 | } 1621 | 1622 | if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block 1623 | int32_t value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); 1624 | Serial.print(F(" Value=0x")); Serial.print(value, HEX); 1625 | Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); 1626 | } 1627 | Serial.println(); 1628 | } 1629 | 1630 | return; 1631 | } // End PICC_DumpMifareClassicSectorToSerial() 1632 | 1633 | /** 1634 | * Dumps memory contents of a MIFARE Ultralight PICC. 1635 | */ 1636 | void MFRC522::PICC_DumpMifareUltralightToSerial() { 1637 | MFRC522::StatusCode status; 1638 | byte byteCount; 1639 | byte buffer[18]; 1640 | byte i; 1641 | 1642 | Serial.println(F("Page 0 1 2 3")); 1643 | // Try the mpages of the original Ultralight. Ultralight C has more pages. 1644 | for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. 1645 | // Read pages 1646 | byteCount = sizeof(buffer); 1647 | status = MIFARE_Read(page, buffer, &byteCount); 1648 | if (status != STATUS_OK) { 1649 | Serial.print(F("MIFARE_Read() failed: ")); 1650 | Serial.println(GetStatusCodeName(status)); 1651 | break; 1652 | } 1653 | // Dump data 1654 | for (byte offset = 0; offset < 4; offset++) { 1655 | i = page + offset; 1656 | if(i < 10) 1657 | Serial.print(F(" ")); // Pad with spaces 1658 | else 1659 | Serial.print(F(" ")); // Pad with spaces 1660 | Serial.print(i); 1661 | Serial.print(F(" ")); 1662 | for (byte index = 0; index < 4; index++) { 1663 | i = 4 * offset + index; 1664 | if(buffer[i] < 0x10) 1665 | Serial.print(F(" 0")); 1666 | else 1667 | Serial.print(F(" ")); 1668 | Serial.print(buffer[i], HEX); 1669 | } 1670 | Serial.println(); 1671 | } 1672 | } 1673 | } // End PICC_DumpMifareUltralightToSerial() 1674 | 1675 | /** 1676 | * 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). 1677 | */ 1678 | void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. 1679 | byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) 1680 | byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) 1681 | byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) 1682 | byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) 1683 | ) { 1684 | byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); 1685 | byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); 1686 | byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); 1687 | 1688 | accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); 1689 | accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); 1690 | accessBitBuffer[2] = c3 << 4 | c2; 1691 | } // End MIFARE_SetAccessBits() 1692 | 1693 | 1694 | /** 1695 | * Performs the "magic sequence" needed to get Chinese UID changeable 1696 | * Mifare cards to allow writing to sector 0, where the card UID is stored. 1697 | * 1698 | * Note that you do not need to have selected the card through REQA or WUPA, 1699 | * this sequence works immediately when the card is in the reader vicinity. 1700 | * This means you can use this method even on "bricked" cards that your reader does 1701 | * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). 1702 | * 1703 | * Of course with non-bricked devices, you're free to select them before calling this function. 1704 | */ 1705 | bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { 1706 | // Magic sequence: 1707 | // > 50 00 57 CD (HALT + CRC) 1708 | // > 40 (7 bits only) 1709 | // < A (4 bits only) 1710 | // > 43 1711 | // < A (4 bits only) 1712 | // Then you can write to sector 0 without authenticating 1713 | 1714 | PICC_HaltA(); // 50 00 57 CD 1715 | 1716 | byte cmd = 0x40; 1717 | byte validBits = 7; /* Our command is only 7 bits. After receiving card response, 1718 | this will contain amount of valid response bits. */ 1719 | byte response[32]; // Card's response is written here 1720 | byte received; 1721 | MFRC522::StatusCode status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 1722 | if(status != STATUS_OK) { 1723 | if(logErrors) { 1724 | Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); 1725 | Serial.print(F("Error name: ")); 1726 | Serial.println(GetStatusCodeName(status)); 1727 | } 1728 | return false; 1729 | } 1730 | if (received != 1 || response[0] != 0x0A) { 1731 | if (logErrors) { 1732 | Serial.print(F("Got bad response on backdoor 0x40 command: ")); 1733 | Serial.print(response[0], HEX); 1734 | Serial.print(F(" (")); 1735 | Serial.print(validBits); 1736 | Serial.print(F(" valid bits)\r\n")); 1737 | } 1738 | return false; 1739 | } 1740 | 1741 | cmd = 0x43; 1742 | validBits = 8; 1743 | status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 1744 | if(status != STATUS_OK) { 1745 | if(logErrors) { 1746 | Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); 1747 | Serial.print(F("Error name: ")); 1748 | Serial.println(GetStatusCodeName(status)); 1749 | } 1750 | return false; 1751 | } 1752 | if (received != 1 || response[0] != 0x0A) { 1753 | if (logErrors) { 1754 | Serial.print(F("Got bad response on backdoor 0x43 command: ")); 1755 | Serial.print(response[0], HEX); 1756 | Serial.print(F(" (")); 1757 | Serial.print(validBits); 1758 | Serial.print(F(" valid bits)\r\n")); 1759 | } 1760 | return false; 1761 | } 1762 | 1763 | // You can now write to sector 0 without authenticating! 1764 | return true; 1765 | } // End MIFARE_OpenUidBackdoor() 1766 | 1767 | /** 1768 | * Reads entire block 0, including all manufacturer data, and overwrites 1769 | * that block with the new UID, a freshly calculated BCC, and the original 1770 | * manufacturer data. 1771 | * 1772 | * It assumes a default KEY A of 0xFFFFFFFFFFFF. 1773 | * Make sure to have selected the card before this function is called. 1774 | */ 1775 | bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { 1776 | 1777 | // UID + BCC byte can not be larger than 16 together 1778 | if (!newUid || !uidSize || uidSize > 15) { 1779 | if (logErrors) { 1780 | Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); 1781 | } 1782 | return false; 1783 | } 1784 | 1785 | // Authenticate for reading 1786 | MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 1787 | MFRC522::StatusCode status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); 1788 | if (status != STATUS_OK) { 1789 | 1790 | if (status == STATUS_TIMEOUT) { 1791 | // We get a read timeout if no card is selected yet, so let's select one 1792 | 1793 | // Wake the card up again if sleeping 1794 | // byte atqa_answer[2]; 1795 | // byte atqa_size = 2; 1796 | // PICC_WakeupA(atqa_answer, &atqa_size); 1797 | 1798 | if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { 1799 | Serial.println(F("No card was previously selected, and none are available. Failed to set UID.")); 1800 | return false; 1801 | } 1802 | 1803 | status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); 1804 | if (status != STATUS_OK) { 1805 | // We tried, time to give up 1806 | if (logErrors) { 1807 | Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); 1808 | Serial.println(GetStatusCodeName(status)); 1809 | } 1810 | return false; 1811 | } 1812 | } 1813 | else { 1814 | if (logErrors) { 1815 | Serial.print(F("PCD_Authenticate() failed: ")); 1816 | Serial.println(GetStatusCodeName(status)); 1817 | } 1818 | return false; 1819 | } 1820 | } 1821 | 1822 | // Read block 0 1823 | byte block0_buffer[18]; 1824 | byte byteCount = sizeof(block0_buffer); 1825 | status = MIFARE_Read((byte)0, block0_buffer, &byteCount); 1826 | if (status != STATUS_OK) { 1827 | if (logErrors) { 1828 | Serial.print(F("MIFARE_Read() failed: ")); 1829 | Serial.println(GetStatusCodeName(status)); 1830 | Serial.println(F("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?")); 1831 | } 1832 | return false; 1833 | } 1834 | 1835 | // Write new UID to the data we just read, and calculate BCC byte 1836 | byte bcc = 0; 1837 | for (uint8_t i = 0; i < uidSize; i++) { 1838 | block0_buffer[i] = newUid[i]; 1839 | bcc ^= newUid[i]; 1840 | } 1841 | 1842 | // Write BCC byte to buffer 1843 | block0_buffer[uidSize] = bcc; 1844 | 1845 | // Stop encrypted traffic so we can send raw bytes 1846 | PCD_StopCrypto1(); 1847 | 1848 | // Activate UID backdoor 1849 | if (!MIFARE_OpenUidBackdoor(logErrors)) { 1850 | if (logErrors) { 1851 | Serial.println(F("Activating the UID backdoor failed.")); 1852 | } 1853 | return false; 1854 | } 1855 | 1856 | // Write modified block 0 back to card 1857 | status = MIFARE_Write((byte)0, block0_buffer, (byte)16); 1858 | if (status != STATUS_OK) { 1859 | if (logErrors) { 1860 | Serial.print(F("MIFARE_Write() failed: ")); 1861 | Serial.println(GetStatusCodeName(status)); 1862 | } 1863 | return false; 1864 | } 1865 | 1866 | // Wake the card up again 1867 | byte atqa_answer[2]; 1868 | byte atqa_size = 2; 1869 | PICC_WakeupA(atqa_answer, &atqa_size); 1870 | 1871 | return true; 1872 | } 1873 | 1874 | /** 1875 | * Resets entire sector 0 to zeroes, so the card can be read again by readers. 1876 | */ 1877 | bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { 1878 | MIFARE_OpenUidBackdoor(logErrors); 1879 | 1880 | byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 1881 | 1882 | // Write modified block 0 back to card 1883 | MFRC522::StatusCode status = MIFARE_Write((byte)0, block0_buffer, (byte)16); 1884 | if (status != STATUS_OK) { 1885 | if (logErrors) { 1886 | Serial.print(F("MIFARE_Write() failed: ")); 1887 | Serial.println(GetStatusCodeName(status)); 1888 | } 1889 | return false; 1890 | } 1891 | return true; 1892 | } 1893 | 1894 | ///////////////////////////////////////////////////////////////////////////////////// 1895 | // Convenience functions - does not add extra functionality 1896 | ///////////////////////////////////////////////////////////////////////////////////// 1897 | 1898 | /** 1899 | * Returns true if a PICC responds to PICC_CMD_REQA. 1900 | * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. 1901 | * 1902 | * @return bool 1903 | */ 1904 | bool MFRC522::PICC_IsNewCardPresent() { 1905 | byte bufferATQA[2]; 1906 | byte bufferSize = sizeof(bufferATQA); 1907 | 1908 | // Reset baud rates 1909 | PCD_WriteRegister(TxModeReg, 0x00); 1910 | PCD_WriteRegister(RxModeReg, 0x00); 1911 | // Reset ModWidthReg 1912 | PCD_WriteRegister(ModWidthReg, 0x26); 1913 | 1914 | MFRC522::StatusCode result = PICC_RequestA(bufferATQA, &bufferSize); 1915 | return (result == STATUS_OK || result == STATUS_COLLISION); 1916 | } // End PICC_IsNewCardPresent() 1917 | 1918 | /** 1919 | * Simple wrapper around PICC_Select. 1920 | * Returns true if a UID could be read. 1921 | * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. 1922 | * The read UID is available in the class variable uid. 1923 | * 1924 | * @return bool 1925 | */ 1926 | bool MFRC522::PICC_ReadCardSerial() { 1927 | MFRC522::StatusCode result = PICC_Select(&uid); 1928 | return (result == STATUS_OK); 1929 | } // End 1930 | -------------------------------------------------------------------------------- /MFRC522.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Library to use Arduino MFRC522 module. 3 | * 4 | * @authors Dr.Leong, Miguel Balboa, Søren Thing Andersen, Tom Clement, many more! See GitLog. 5 | * 6 | * For more information read the README. 7 | * 8 | * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. 9 | */ 10 | #ifndef MFRC522_h 11 | #define MFRC522_h 12 | 13 | #include "require_cpp11.h" 14 | #include "deprecated.h" 15 | // Enable integer limits 16 | #define __STDC_LIMIT_MACROS 17 | #include 18 | #include 19 | #include 20 | 21 | #ifndef MFRC522_SPICLOCK 22 | #define MFRC522_SPICLOCK (4000000u) // MFRC522 accept upto 10MHz, set to 4MHz. 23 | #endif 24 | 25 | // Firmware data for self-test 26 | // Reference values based on firmware version 27 | // Hint: if needed, you can remove unused self-test data to save flash memory 28 | // 29 | // Version 0.0 (0x90) 30 | // Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 self-test 31 | const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { 32 | 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, 33 | 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, 34 | 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, 35 | 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, 36 | 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, 37 | 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, 38 | 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, 39 | 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D 40 | }; 41 | // Version 1.0 (0x91) 42 | // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test 43 | const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { 44 | 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, 45 | 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, 46 | 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, 47 | 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, 48 | 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, 49 | 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, 50 | 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, 51 | 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 52 | }; 53 | // Version 2.0 (0x92) 54 | // NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test 55 | const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { 56 | 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, 57 | 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, 58 | 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, 59 | 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, 60 | 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, 61 | 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, 62 | 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, 63 | 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F 64 | }; 65 | // Clone 66 | // Fudan Semiconductor FM17522 (0x88) 67 | const byte FM17522_firmware_reference[] PROGMEM = { 68 | 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, 69 | 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, 70 | 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, 71 | 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, 72 | 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, 73 | 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, 74 | 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, 75 | 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 76 | }; 77 | 78 | class MFRC522 { 79 | public: 80 | // Size of the MFRC522 FIFO 81 | static constexpr byte FIFO_SIZE = 64; // The FIFO is 64 bytes. 82 | // Default value for unused pin 83 | static constexpr uint8_t UNUSED_PIN = UINT8_MAX; 84 | 85 | // MFRC522 registers. Described in chapter 9 of the datasheet. 86 | // When using SPI all addresses are shifted one bit left in the "SPI address byte" (section 8.1.2.3) 87 | enum PCD_Register : byte { 88 | // Page 0: Command and status 89 | // 0x00 // reserved for future use 90 | CommandReg = 0x01 << 1, // starts and stops command execution 91 | ComIEnReg = 0x02 << 1, // enable and disable interrupt request control bits 92 | DivIEnReg = 0x03 << 1, // enable and disable interrupt request control bits 93 | ComIrqReg = 0x04 << 1, // interrupt request bits 94 | DivIrqReg = 0x05 << 1, // interrupt request bits 95 | ErrorReg = 0x06 << 1, // error bits showing the error status of the last command executed 96 | Status1Reg = 0x07 << 1, // communication status bits 97 | Status2Reg = 0x08 << 1, // receiver and transmitter status bits 98 | FIFODataReg = 0x09 << 1, // input and output of 64 byte FIFO buffer 99 | FIFOLevelReg = 0x0A << 1, // number of bytes stored in the FIFO buffer 100 | WaterLevelReg = 0x0B << 1, // level for FIFO underflow and overflow warning 101 | ControlReg = 0x0C << 1, // miscellaneous control registers 102 | BitFramingReg = 0x0D << 1, // adjustments for bit-oriented frames 103 | CollReg = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface 104 | // 0x0F // reserved for future use 105 | 106 | // Page 1: Command 107 | // 0x10 // reserved for future use 108 | ModeReg = 0x11 << 1, // defines general modes for transmitting and receiving 109 | TxModeReg = 0x12 << 1, // defines transmission data rate and framing 110 | RxModeReg = 0x13 << 1, // defines reception data rate and framing 111 | TxControlReg = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2 112 | TxASKReg = 0x15 << 1, // controls the setting of the transmission modulation 113 | TxSelReg = 0x16 << 1, // selects the internal sources for the antenna driver 114 | RxSelReg = 0x17 << 1, // selects internal receiver settings 115 | RxThresholdReg = 0x18 << 1, // selects thresholds for the bit decoder 116 | DemodReg = 0x19 << 1, // defines demodulator settings 117 | // 0x1A // reserved for future use 118 | // 0x1B // reserved for future use 119 | MfTxReg = 0x1C << 1, // controls some MIFARE communication transmit parameters 120 | MfRxReg = 0x1D << 1, // controls some MIFARE communication receive parameters 121 | // 0x1E // reserved for future use 122 | SerialSpeedReg = 0x1F << 1, // selects the speed of the serial UART interface 123 | 124 | // Page 2: Configuration 125 | // 0x20 // reserved for future use 126 | CRCResultRegH = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation 127 | CRCResultRegL = 0x22 << 1, 128 | // 0x23 // reserved for future use 129 | ModWidthReg = 0x24 << 1, // controls the ModWidth setting? 130 | // 0x25 // reserved for future use 131 | RFCfgReg = 0x26 << 1, // configures the receiver gain 132 | GsNReg = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation 133 | CWGsPReg = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation 134 | ModGsPReg = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation 135 | TModeReg = 0x2A << 1, // defines settings for the internal timer 136 | TPrescalerReg = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. 137 | TReloadRegH = 0x2C << 1, // defines the 16-bit timer reload value 138 | TReloadRegL = 0x2D << 1, 139 | TCounterValueRegH = 0x2E << 1, // shows the 16-bit timer value 140 | TCounterValueRegL = 0x2F << 1, 141 | 142 | // Page 3: Test Registers 143 | // 0x30 // reserved for future use 144 | TestSel1Reg = 0x31 << 1, // general test signal configuration 145 | TestSel2Reg = 0x32 << 1, // general test signal configuration 146 | TestPinEnReg = 0x33 << 1, // enables pin output driver on pins D1 to D7 147 | TestPinValueReg = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus 148 | TestBusReg = 0x35 << 1, // shows the status of the internal test bus 149 | AutoTestReg = 0x36 << 1, // controls the digital self-test 150 | VersionReg = 0x37 << 1, // shows the software version 151 | AnalogTestReg = 0x38 << 1, // controls the pins AUX1 and AUX2 152 | TestDAC1Reg = 0x39 << 1, // defines the test value for TestDAC1 153 | TestDAC2Reg = 0x3A << 1, // defines the test value for TestDAC2 154 | TestADCReg = 0x3B << 1 // shows the value of ADC I and Q channels 155 | // 0x3C // reserved for production tests 156 | // 0x3D // reserved for production tests 157 | // 0x3E // reserved for production tests 158 | // 0x3F // reserved for production tests 159 | }; 160 | 161 | // MFRC522 commands. Described in chapter 10 of the datasheet. 162 | enum PCD_Command : byte { 163 | PCD_Idle = 0x00, // no action, cancels current command execution 164 | PCD_Mem = 0x01, // stores 25 bytes into the internal buffer 165 | PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number 166 | PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self-test 167 | PCD_Transmit = 0x04, // transmits data from the FIFO buffer 168 | PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit 169 | PCD_Receive = 0x08, // activates the receiver circuits 170 | PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission 171 | PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader 172 | PCD_SoftReset = 0x0F // resets the MFRC522 173 | }; 174 | 175 | // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). 176 | // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf 177 | enum PCD_RxGain : byte { 178 | RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum 179 | RxGain_23dB = 0x01 << 4, // 001b - 23 dB 180 | RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b 181 | RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b 182 | RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default 183 | RxGain_38dB = 0x05 << 4, // 101b - 38 dB 184 | RxGain_43dB = 0x06 << 4, // 110b - 43 dB 185 | RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum 186 | RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB 187 | RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB 188 | RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB 189 | }; 190 | 191 | // Commands sent to the PICC. 192 | enum PICC_Command : byte { 193 | // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) 194 | 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. 195 | 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. 196 | PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. 197 | PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 198 | PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 199 | PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 200 | PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. 201 | PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. 202 | // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) 203 | // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. 204 | // The read/write commands can also be used for MIFARE Ultralight. 205 | PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A 206 | PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B 207 | PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. 208 | PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. 209 | PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. 210 | PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. 211 | PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. 212 | PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. 213 | // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) 214 | // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. 215 | PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. 216 | }; 217 | 218 | // MIFARE constants that does not fit anywhere else 219 | enum MIFARE_Misc { 220 | MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. 221 | MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. 222 | }; 223 | 224 | // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. 225 | // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered 226 | enum PICC_Type : byte { 227 | PICC_TYPE_UNKNOWN , 228 | PICC_TYPE_ISO_14443_4 , // PICC compliant with ISO/IEC 14443-4 229 | PICC_TYPE_ISO_18092 , // PICC compliant with ISO/IEC 18092 (NFC) 230 | PICC_TYPE_MIFARE_MINI , // MIFARE Classic protocol, 320 bytes 231 | PICC_TYPE_MIFARE_1K , // MIFARE Classic protocol, 1KB 232 | PICC_TYPE_MIFARE_4K , // MIFARE Classic protocol, 4KB 233 | PICC_TYPE_MIFARE_UL , // MIFARE Ultralight or Ultralight C 234 | PICC_TYPE_MIFARE_PLUS , // MIFARE Plus 235 | PICC_TYPE_MIFARE_DESFIRE, // MIFARE DESFire 236 | PICC_TYPE_TNP3XXX , // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure 237 | PICC_TYPE_NOT_COMPLETE = 0xff // SAK indicates UID is not complete. 238 | }; 239 | 240 | // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. 241 | // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered 242 | enum StatusCode : byte { 243 | STATUS_OK , // Success 244 | STATUS_ERROR , // Error in communication 245 | STATUS_COLLISION , // Collission detected 246 | STATUS_TIMEOUT , // Timeout in communication. 247 | STATUS_NO_ROOM , // A buffer is not big enough. 248 | STATUS_INTERNAL_ERROR , // Internal error in the code. Should not happen ;-) 249 | STATUS_INVALID , // Invalid argument. 250 | STATUS_CRC_WRONG , // The CRC_A does not match 251 | STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK. 252 | }; 253 | 254 | // A struct used for passing the UID of a PICC. 255 | typedef struct { 256 | byte size; // Number of bytes in the UID. 4, 7 or 10. 257 | byte uidByte[10]; 258 | byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. 259 | } Uid; 260 | 261 | // A struct used for passing a MIFARE Crypto1 key 262 | typedef struct { 263 | byte keyByte[MF_KEY_SIZE]; 264 | } MIFARE_Key; 265 | 266 | // Member variables 267 | Uid uid; // Used by PICC_ReadCardSerial(). 268 | 269 | ///////////////////////////////////////////////////////////////////////////////////// 270 | // Functions for setting up the Arduino 271 | ///////////////////////////////////////////////////////////////////////////////////// 272 | MFRC522(); 273 | MFRC522(byte resetPowerDownPin); 274 | MFRC522(byte chipSelectPin, byte resetPowerDownPin); 275 | 276 | ///////////////////////////////////////////////////////////////////////////////////// 277 | // Basic interface functions for communicating with the MFRC522 278 | ///////////////////////////////////////////////////////////////////////////////////// 279 | void PCD_WriteRegister(PCD_Register reg, byte value); 280 | void PCD_WriteRegister(PCD_Register reg, byte count, byte *values); 281 | byte PCD_ReadRegister(PCD_Register reg); 282 | void PCD_ReadRegister(PCD_Register reg, byte count, byte *values, byte rxAlign = 0); 283 | void PCD_SetRegisterBitMask(PCD_Register reg, byte mask); 284 | void PCD_ClearRegisterBitMask(PCD_Register reg, byte mask); 285 | StatusCode PCD_CalculateCRC(byte *data, byte length, byte *result); 286 | 287 | ///////////////////////////////////////////////////////////////////////////////////// 288 | // Functions for manipulating the MFRC522 289 | ///////////////////////////////////////////////////////////////////////////////////// 290 | void PCD_Init(); 291 | void PCD_Init(byte resetPowerDownPin); 292 | void PCD_Init(byte chipSelectPin, byte resetPowerDownPin); 293 | void PCD_Reset(); 294 | void PCD_AntennaOn(); 295 | void PCD_AntennaOff(); 296 | byte PCD_GetAntennaGain(); 297 | void PCD_SetAntennaGain(byte mask); 298 | bool PCD_PerformSelfTest(); 299 | 300 | ///////////////////////////////////////////////////////////////////////////////////// 301 | // Power control functions 302 | ///////////////////////////////////////////////////////////////////////////////////// 303 | void PCD_SoftPowerDown(); 304 | void PCD_SoftPowerUp(); 305 | 306 | ///////////////////////////////////////////////////////////////////////////////////// 307 | // Functions for communicating with PICCs 308 | ///////////////////////////////////////////////////////////////////////////////////// 309 | StatusCode PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); 310 | StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = nullptr, byte *backLen = nullptr, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); 311 | StatusCode PICC_RequestA(byte *bufferATQA, byte *bufferSize); 312 | StatusCode PICC_WakeupA(byte *bufferATQA, byte *bufferSize); 313 | StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); 314 | virtual StatusCode PICC_Select(Uid *uid, byte validBits = 0); 315 | StatusCode PICC_HaltA(); 316 | 317 | ///////////////////////////////////////////////////////////////////////////////////// 318 | // Functions for communicating with MIFARE PICCs 319 | ///////////////////////////////////////////////////////////////////////////////////// 320 | StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); 321 | void PCD_StopCrypto1(); 322 | StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); 323 | StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); 324 | StatusCode MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); 325 | StatusCode MIFARE_Decrement(byte blockAddr, int32_t delta); 326 | StatusCode MIFARE_Increment(byte blockAddr, int32_t delta); 327 | StatusCode MIFARE_Restore(byte blockAddr); 328 | StatusCode MIFARE_Transfer(byte blockAddr); 329 | StatusCode MIFARE_GetValue(byte blockAddr, int32_t *value); 330 | StatusCode MIFARE_SetValue(byte blockAddr, int32_t value); 331 | StatusCode PCD_NTAG216_AUTH(byte *passWord, byte pACK[]); 332 | 333 | ///////////////////////////////////////////////////////////////////////////////////// 334 | // Support functions 335 | ///////////////////////////////////////////////////////////////////////////////////// 336 | StatusCode PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); 337 | // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory 338 | //const char *GetStatusCodeName(byte code); 339 | static const __FlashStringHelper *GetStatusCodeName(StatusCode code); 340 | static PICC_Type PICC_GetType(byte sak); 341 | // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory 342 | //const char *PICC_GetTypeName(byte type); 343 | static const __FlashStringHelper *PICC_GetTypeName(PICC_Type type); 344 | 345 | // Support functions for debuging 346 | void PCD_DumpVersionToSerial(); 347 | void PICC_DumpToSerial(Uid *uid); 348 | void PICC_DumpDetailsToSerial(Uid *uid); 349 | void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); 350 | void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); 351 | void PICC_DumpMifareUltralightToSerial(); 352 | 353 | // Advanced functions for MIFARE 354 | void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); 355 | bool MIFARE_OpenUidBackdoor(bool logErrors); 356 | bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); 357 | bool MIFARE_UnbrickUidSector(bool logErrors); 358 | 359 | ///////////////////////////////////////////////////////////////////////////////////// 360 | // Convenience functions - does not add extra functionality 361 | ///////////////////////////////////////////////////////////////////////////////////// 362 | virtual bool PICC_IsNewCardPresent(); 363 | virtual bool PICC_ReadCardSerial(); 364 | 365 | protected: 366 | byte _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) 367 | byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) 368 | StatusCode MIFARE_TwoStepHelper(byte command, byte blockAddr, int32_t data); 369 | }; 370 | 371 | #endif 372 | -------------------------------------------------------------------------------- /MFRC522Extended.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Library extends MFRC522.h to support RATS for ISO-14443-4 PICC. 3 | * RATS - Request for Answer To Select. 4 | * NOTE: Please also check the comments in MFRC522Extended.h 5 | * @author JPG-Consulting 6 | */ 7 | 8 | #include "MFRC522Extended.h" 9 | 10 | ///////////////////////////////////////////////////////////////////////////////////// 11 | // Functions for communicating with PICCs 12 | ///////////////////////////////////////////////////////////////////////////////////// 13 | 14 | /** 15 | * Transmits SELECT/ANTICOLLISION commands to select a single PICC. 16 | * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). 17 | * On success: 18 | * - 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.) 19 | * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. 20 | * 21 | * A PICC UID consists of 4, 7 or 10 bytes. 22 | * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: 23 | * UID size Number of UID bytes Cascade levels Example of PICC 24 | * ======== =================== ============== =============== 25 | * single 4 1 MIFARE Classic 26 | * double 7 2 MIFARE Ultralight 27 | * triple 10 3 Not currently in use? 28 | * 29 | * @return STATUS_OK on success, STATUS_??? otherwise. 30 | */ 31 | MFRC522::StatusCode MFRC522Extended::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. 32 | byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. 33 | ) { 34 | bool uidComplete; 35 | bool selectDone; 36 | bool useCascadeTag; 37 | byte cascadeLevel = 1; 38 | MFRC522::StatusCode result; 39 | byte count; 40 | byte index; 41 | byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. 42 | int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. 43 | byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A 44 | byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. 45 | byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. 46 | byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. 47 | byte *responseBuffer; 48 | byte responseLength; 49 | 50 | // Description of buffer structure: 51 | // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 52 | // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. 53 | // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. 54 | // Byte 3: UID-data 55 | // Byte 4: UID-data 56 | // Byte 5: UID-data 57 | // Byte 6: BCC Block Check Character - XOR of bytes 2-5 58 | // Byte 7: CRC_A 59 | // Byte 8: CRC_A 60 | // The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level. 61 | // 62 | // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) 63 | // UID size Cascade level Byte2 Byte3 Byte4 Byte5 64 | // ======== ============= ===== ===== ===== ===== 65 | // 4 bytes 1 uid0 uid1 uid2 uid3 66 | // 7 bytes 1 CT uid0 uid1 uid2 67 | // 2 uid3 uid4 uid5 uid6 68 | // 10 bytes 1 CT uid0 uid1 uid2 69 | // 2 CT uid3 uid4 uid5 70 | // 3 uid6 uid7 uid8 uid9 71 | 72 | // Sanity checks 73 | if (validBits > 80) { 74 | return STATUS_INVALID; 75 | } 76 | 77 | // Prepare MFRC522 78 | PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. 79 | 80 | // Repeat Cascade Level loop until we have a complete UID. 81 | uidComplete = false; 82 | while (!uidComplete) { 83 | // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. 84 | switch (cascadeLevel) { 85 | case 1: 86 | buffer[0] = PICC_CMD_SEL_CL1; 87 | uidIndex = 0; 88 | useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes 89 | break; 90 | 91 | case 2: 92 | buffer[0] = PICC_CMD_SEL_CL2; 93 | uidIndex = 3; 94 | useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes 95 | break; 96 | 97 | case 3: 98 | buffer[0] = PICC_CMD_SEL_CL3; 99 | uidIndex = 6; 100 | useCascadeTag = false; // Never used in CL3. 101 | break; 102 | 103 | default: 104 | return STATUS_INTERNAL_ERROR; 105 | break; 106 | } 107 | 108 | // How many UID bits are known in this Cascade Level? 109 | currentLevelKnownBits = validBits - (8 * uidIndex); 110 | if (currentLevelKnownBits < 0) { 111 | currentLevelKnownBits = 0; 112 | } 113 | // Copy the known bits from uid->uidByte[] to buffer[] 114 | index = 2; // destination index in buffer[] 115 | if (useCascadeTag) { 116 | buffer[index++] = PICC_CMD_CT; 117 | } 118 | byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. 119 | if (bytesToCopy) { 120 | byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag 121 | if (bytesToCopy > maxBytes) { 122 | bytesToCopy = maxBytes; 123 | } 124 | for (count = 0; count < bytesToCopy; count++) { 125 | buffer[index++] = uid->uidByte[uidIndex + count]; 126 | } 127 | } 128 | // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits 129 | if (useCascadeTag) { 130 | currentLevelKnownBits += 8; 131 | } 132 | 133 | // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. 134 | selectDone = false; 135 | while (!selectDone) { 136 | // Find out how many bits and bytes to send and receive. 137 | if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. 138 | //Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 139 | buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes 140 | // Calculate BCC - Block Check Character 141 | buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; 142 | // Calculate CRC_A 143 | result = PCD_CalculateCRC(buffer, 7, &buffer[7]); 144 | if (result != STATUS_OK) { 145 | return result; 146 | } 147 | txLastBits = 0; // 0 => All 8 bits are valid. 148 | bufferUsed = 9; 149 | // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) 150 | responseBuffer = &buffer[6]; 151 | responseLength = 3; 152 | } 153 | else { // This is an ANTICOLLISION. 154 | //Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); 155 | txLastBits = currentLevelKnownBits % 8; 156 | count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. 157 | index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs 158 | buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits 159 | bufferUsed = index + (txLastBits ? 1 : 0); 160 | // Store response in the unused part of buffer 161 | responseBuffer = &buffer[index]; 162 | responseLength = sizeof(buffer) - index; 163 | } 164 | 165 | // Set bit adjustments 166 | rxAlign = txLastBits; // Having a separate variable is overkill. But it makes the next line easier to read. 167 | PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] 168 | 169 | // Transmit the buffer and receive the response. 170 | result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); 171 | if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. 172 | byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] 173 | if (valueOfCollReg & 0x20) { // CollPosNotValid 174 | return STATUS_COLLISION; // Without a valid collision position we cannot continue 175 | } 176 | byte collisionPos = valueOfCollReg & 0x1F; // Values 0-31, 0 means bit 32. 177 | if (collisionPos == 0) { 178 | collisionPos = 32; 179 | } 180 | if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen 181 | return STATUS_INTERNAL_ERROR; 182 | } 183 | // Choose the PICC with the bit set. 184 | currentLevelKnownBits = collisionPos; 185 | count = (currentLevelKnownBits - 1) % 8; // The bit to modify 186 | index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. 187 | buffer[index] |= (1 << count); 188 | } 189 | else if (result != STATUS_OK) { 190 | return result; 191 | } 192 | else { // STATUS_OK 193 | if (currentLevelKnownBits >= 32) { // This was a SELECT. 194 | selectDone = true; // No more anticollision 195 | // We continue below outside the while. 196 | } 197 | else { // This was an ANTICOLLISION. 198 | // We now have all 32 bits of the UID in this Cascade Level 199 | currentLevelKnownBits = 32; 200 | // Run loop again to do the SELECT. 201 | } 202 | } 203 | } // End of while (!selectDone) 204 | 205 | // We do not check the CBB - it was constructed by us above. 206 | 207 | // Copy the found UID bytes from buffer[] to uid->uidByte[] 208 | index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] 209 | bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; 210 | for (count = 0; count < bytesToCopy; count++) { 211 | uid->uidByte[uidIndex + count] = buffer[index++]; 212 | } 213 | 214 | // Check response SAK (Select Acknowledge) 215 | if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). 216 | return STATUS_ERROR; 217 | } 218 | // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. 219 | result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); 220 | if (result != STATUS_OK) { 221 | return result; 222 | } 223 | if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { 224 | return STATUS_CRC_WRONG; 225 | } 226 | if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes 227 | cascadeLevel++; 228 | } 229 | else { 230 | uidComplete = true; 231 | uid->sak = responseBuffer[0]; 232 | } 233 | } // End of while (!uidComplete) 234 | 235 | // Set correct uid->size 236 | uid->size = 3 * cascadeLevel + 1; 237 | 238 | // IF SAK bit 6 = 1 then it is ISO/IEC 14443-4 (T=CL) 239 | // A Request ATS command should be sent 240 | // We also check SAK bit 3 is cero, as it stands for UID complete (1 would tell us it is incomplete) 241 | if ((uid->sak & 0x24) == 0x20) { 242 | Ats ats; 243 | result = PICC_RequestATS(&ats); 244 | if (result == STATUS_OK) { 245 | // Check the ATS 246 | if (ats.size > 0) 247 | { 248 | // TA1 has been transmitted? 249 | // PPS must be supported... 250 | if (ats.ta1.transmitted) 251 | { 252 | // TA1 253 | // 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Description 254 | // ---+---+---+---+---+---+---+---+------------------------------------------ 255 | // 0 | - | - | - | 0 | - | - | - | Different D for each direction supported 256 | // 1 | - | - | - | 0 | - | - | - | Only same D for both direction supported 257 | // - | x | x | x | 0 | - | - | - | DS (Send D) 258 | // - | - | - | - | 0 | x | x | x | DR (Receive D) 259 | // 260 | // D to bitrate table 261 | // 3 | 2 | 1 | Value 262 | // ---+---+---+----------------------------- 263 | // 1 | - | - | 848 kBaud is supported 264 | // - | 1 | - | 424 kBaud is supported 265 | // - | - | 1 | 212 kBaud is supported 266 | // 0 | 0 | 0 | Only 106 kBaud is supported 267 | // 268 | // Note: 106 kBaud is always supported 269 | // 270 | // I have almost constant timeouts when changing speeds :( 271 | // default never used, so only delarate 272 | //TagBitRates ds = BITRATE_106KBITS; 273 | //TagBitRates dr = BITRATE_106KBITS; 274 | TagBitRates ds; 275 | TagBitRates dr; 276 | 277 | //// TODO Not working at 848 or 424 278 | //if (ats.ta1.ds & 0x04) 279 | //{ 280 | // ds = BITRATE_848KBITS; 281 | //} 282 | //else if (ats.ta1.ds & 0x02) 283 | //{ 284 | // ds = BITRATE_424KBITS; 285 | //} 286 | //else if (ats.ta1.ds & 0x01) 287 | //{ 288 | // ds = BITRATE_212KBITS; 289 | //} 290 | //else 291 | //{ 292 | // ds = BITRATE_106KBITS; 293 | //} 294 | 295 | if (ats.ta1.ds & 0x01) 296 | { 297 | ds = BITRATE_212KBITS; 298 | } 299 | else 300 | { 301 | ds = BITRATE_106KBITS; 302 | } 303 | 304 | //// Not working at 848 or 424 305 | //if (ats.ta1.dr & 0x04) 306 | //{ 307 | // dr = BITRATE_848KBITS; 308 | //} 309 | //else if (ats.ta1.dr & 0x02) 310 | //{ 311 | // dr = BITRATE_424KBITS; 312 | //} 313 | //else if (ats.ta1.dr & 0x01) 314 | //{ 315 | // dr = BITRATE_212KBITS; 316 | //} 317 | //else 318 | //{ 319 | // dr = BITRATE_106KBITS; 320 | //} 321 | 322 | if (ats.ta1.dr & 0x01) 323 | { 324 | dr = BITRATE_212KBITS; 325 | } 326 | else 327 | { 328 | dr = BITRATE_106KBITS; 329 | } 330 | 331 | PICC_PPS(ds, dr); 332 | } 333 | } 334 | } 335 | } 336 | 337 | return STATUS_OK; 338 | } // End PICC_Select() 339 | 340 | /** 341 | * Transmits a Request command for Answer To Select (ATS). 342 | * 343 | * @return STATUS_OK on success, STATUS_??? otherwise. 344 | */ 345 | MFRC522::StatusCode MFRC522Extended::PICC_RequestATS(Ats *ats) 346 | { 347 | // TODO unused variable 348 | //byte count; 349 | MFRC522::StatusCode result; 350 | 351 | byte bufferATS[FIFO_SIZE]; 352 | byte bufferSize = FIFO_SIZE; 353 | 354 | memset(bufferATS, 0, FIFO_SIZE); 355 | 356 | // Build command buffer 357 | bufferATS[0] = PICC_CMD_RATS; 358 | 359 | // The CID defines the logical number of the addressed card and has a range of 0 360 | // through 14; 15 is reserved for future use (RFU). 361 | // 362 | // FSDI codes the maximum frame size (FSD) that the terminal can receive. 363 | // 364 | // FSDI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F 365 | // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- 366 | // FSD (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 367 | // 368 | bufferATS[1] = 0x50; // FSD=64, CID=0 369 | 370 | // Calculate CRC_A 371 | result = PCD_CalculateCRC(bufferATS, 2, &bufferATS[2]); 372 | if (result != STATUS_OK) { 373 | return result; 374 | } 375 | 376 | // Transmit the buffer and receive the response, validate CRC_A. 377 | result = PCD_TransceiveData(bufferATS, 4, bufferATS, &bufferSize, NULL, 0, true); 378 | if (result != STATUS_OK) { 379 | PICC_HaltA(); 380 | } 381 | 382 | // Set the ats structure data 383 | ats->size = bufferATS[0]; 384 | 385 | // T0 byte: 386 | // 387 | // b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | Meaning 388 | //----+----+----+----+----+----+----+----+--------------------------- 389 | // 0 | ...| ...| ...| ...|... | ...| ...| Set to 0 (RFU) 390 | // 0 | 1 | x | x | ...|... | ...| ...| TC1 transmitted 391 | // 0 | x | 1 | x | ...|... | ...| ...| TB1 transmitted 392 | // 0 | x | x | 1 | ...|... | ...| ...| TA1 transmitted 393 | // 0 | ...| ...| ...| x | x | x | x | Maximum frame size (FSCI) 394 | // 395 | // FSCI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F 396 | // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- 397 | // FSC (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 398 | // 399 | // Default FSCI is 2 (32 bytes) 400 | if (ats->size > 0x01) 401 | { 402 | // TC1, TB1 and TA1 where NOT transmitted 403 | ats->ta1.transmitted = (bool)(bufferATS[1] & 0x40); 404 | ats->tb1.transmitted = (bool)(bufferATS[1] & 0x20); 405 | ats->tc1.transmitted = (bool)(bufferATS[1] & 0x10); 406 | 407 | // Decode FSCI 408 | switch (bufferATS[1] & 0x0F) 409 | { 410 | case 0x00: 411 | ats->fsc = 16; 412 | break; 413 | case 0x01: 414 | ats->fsc = 24; 415 | break; 416 | case 0x02: 417 | ats->fsc = 32; 418 | break; 419 | case 0x03: 420 | ats->fsc = 40; 421 | break; 422 | case 0x04: 423 | ats->fsc = 48; 424 | break; 425 | case 0x05: 426 | ats->fsc = 64; 427 | break; 428 | case 0x06: 429 | ats->fsc = 96; 430 | break; 431 | case 0x07: 432 | ats->fsc = 128; 433 | break; 434 | case 0x08: 435 | // This value cannot be hold by a byte 436 | // The reason I ignore it is that MFRC255 FIFO is 64 bytes so this is not a possible value (or atleast it shouldn't) 437 | //ats->fsc = 256; 438 | break; 439 | // TODO: What to do with RFU (Reserved for future use)? 440 | default: 441 | break; 442 | } 443 | 444 | // TA1 445 | if (ats->ta1.transmitted) 446 | { 447 | ats->ta1.sameD = (bool)(bufferATS[2] & 0x80); 448 | ats->ta1.ds = (TagBitRates)((bufferATS[2] & 0x70) >> 4); 449 | ats->ta1.dr = (TagBitRates)(bufferATS[2] & 0x07); 450 | } 451 | else 452 | { 453 | // Default TA1 454 | ats->ta1.ds = BITRATE_106KBITS; 455 | ats->ta1.dr = BITRATE_106KBITS; 456 | } 457 | 458 | // TB1 459 | if (ats->tb1.transmitted) 460 | { 461 | uint8_t tb1Index = 2; 462 | 463 | if (ats->ta1.transmitted) 464 | tb1Index++; 465 | 466 | ats->tb1.fwi = (bufferATS[tb1Index] & 0xF0) >> 4; 467 | ats->tb1.sfgi = bufferATS[tb1Index] & 0x0F; 468 | } 469 | else 470 | { 471 | // Defaults for TB1 472 | ats->tb1.fwi = 0; // TODO: Don't know the default for this! 473 | ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) 474 | } 475 | 476 | // TC1 477 | if (ats->tc1.transmitted) 478 | { 479 | uint8_t tc1Index = 2; 480 | 481 | if (ats->ta1.transmitted) 482 | tc1Index++; 483 | if (ats->tb1.transmitted) 484 | tc1Index++; 485 | 486 | ats->tc1.supportsCID = (bool)(bufferATS[tc1Index] & 0x02); 487 | ats->tc1.supportsNAD = (bool)(bufferATS[tc1Index] & 0x01); 488 | } 489 | else 490 | { 491 | // Defaults for TC1 492 | ats->tc1.supportsCID = true; 493 | ats->tc1.supportsNAD = false; 494 | } 495 | } 496 | else 497 | { 498 | // TC1, TB1 and TA1 where NOT transmitted 499 | ats->ta1.transmitted = false; 500 | ats->tb1.transmitted = false; 501 | ats->tc1.transmitted = false; 502 | 503 | // Default FSCI 504 | ats->fsc = 32; // Defaults to FSCI 2 (32 bytes) 505 | 506 | // Default TA1 507 | ats->ta1.sameD = false; 508 | ats->ta1.ds = BITRATE_106KBITS; 509 | ats->ta1.dr = BITRATE_106KBITS; 510 | 511 | // Defaults for TB1 512 | ats->tb1.transmitted = false; 513 | ats->tb1.fwi = 0; // TODO: Don't know the default for this! 514 | ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) 515 | 516 | // Defaults for TC1 517 | ats->tc1.transmitted = false; 518 | ats->tc1.supportsCID = true; 519 | ats->tc1.supportsNAD = false; 520 | } 521 | 522 | memcpy(ats->data, bufferATS, bufferSize - 2); 523 | 524 | return result; 525 | } // End PICC_RequestATS() 526 | 527 | /** 528 | * Transmits Protocol and Parameter Selection Request (PPS) without parameter 1 529 | * 530 | * @return STATUS_OK on success, STATUS_??? otherwise. 531 | */ 532 | MFRC522::StatusCode MFRC522Extended::PICC_PPS() 533 | { 534 | StatusCode result; 535 | 536 | byte ppsBuffer[4]; 537 | byte ppsBufferSize = 4; 538 | // Start byte: The start byte (PPS) consists of two parts: 539 | // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. 540 | // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. 541 | ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS 542 | ppsBuffer[1] = 0x00; // PPS0 indicates whether PPS1 is present 543 | 544 | // Calculate CRC_A 545 | result = PCD_CalculateCRC(ppsBuffer, 2, &ppsBuffer[2]); 546 | if (result != STATUS_OK) { 547 | return result; 548 | } 549 | 550 | // Transmit the buffer and receive the response, validate CRC_A. 551 | result = PCD_TransceiveData(ppsBuffer, 4, ppsBuffer, &ppsBufferSize, NULL, 0, true); 552 | if (result == STATUS_OK) 553 | { 554 | // Enable CRC for T=CL 555 | byte txReg = PCD_ReadRegister(TxModeReg) | 0x80; 556 | byte rxReg = PCD_ReadRegister(RxModeReg) | 0x80; 557 | 558 | PCD_WriteRegister(TxModeReg, txReg); 559 | PCD_WriteRegister(RxModeReg, rxReg); 560 | } 561 | 562 | return result; 563 | } // End PICC_PPS() 564 | 565 | /** 566 | * Transmits Protocol and Parameter Selection Request (PPS) 567 | * 568 | * @return STATUS_OK on success, STATUS_??? otherwise. 569 | */ 570 | MFRC522::StatusCode MFRC522Extended::PICC_PPS(TagBitRates sendBitRate, ///< DS 571 | TagBitRates receiveBitRate ///< DR 572 | ) { 573 | StatusCode result; 574 | 575 | // TODO not used 576 | //byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; 577 | //byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; 578 | 579 | byte ppsBuffer[5]; 580 | byte ppsBufferSize = 5; 581 | // Start byte: The start byte (PPS) consists of two parts: 582 | // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. 583 | // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. 584 | ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS 585 | ppsBuffer[1] = 0x11; // PPS0 indicates whether PPS1 is present 586 | 587 | // Bit 8 - Set to '0' as MFRC522 allows different bit rates for send and receive 588 | // Bit 4 - Set to '0' as it is Reserved for future use. 589 | //ppsBuffer[2] = (((sendBitRate & 0x03) << 4) | (receiveBitRate & 0x03)) & 0xE7; 590 | ppsBuffer[2] = (((sendBitRate & 0x03) << 2) | (receiveBitRate & 0x03)) & 0xE7; 591 | 592 | // Calculate CRC_A 593 | result = PCD_CalculateCRC(ppsBuffer, 3, &ppsBuffer[3]); 594 | if (result != STATUS_OK) { 595 | return result; 596 | } 597 | 598 | // Transmit the buffer and receive the response, validate CRC_A. 599 | result = PCD_TransceiveData(ppsBuffer, 5, ppsBuffer, &ppsBufferSize, NULL, 0, true); 600 | if (result == STATUS_OK) 601 | { 602 | // Make sure it is an answer to our PPS 603 | // We should receive our PPS byte and 2 CRC bytes 604 | if ((ppsBufferSize == 3) && (ppsBuffer[0] == 0xD0)) { 605 | byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; 606 | byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; 607 | 608 | // Set bit rate and enable CRC for T=CL 609 | txReg = (txReg & 0x8F) | ((receiveBitRate & 0x03) << 4) | 0x80; 610 | rxReg = (rxReg & 0x8F) | ((sendBitRate & 0x03) << 4) | 0x80; 611 | rxReg &= 0xF0; //Enforce although this should be set already 612 | 613 | // From ConfigIsoType 614 | //rxReg |= 0x06; 615 | 616 | PCD_WriteRegister(TxModeReg, txReg); 617 | PCD_WriteRegister(RxModeReg, rxReg); 618 | 619 | // At 212kBps 620 | switch (sendBitRate) { 621 | case BITRATE_212KBITS: 622 | { 623 | //PCD_WriteRegister(ModWidthReg, 0x13); 624 | PCD_WriteRegister(ModWidthReg, 0x15); 625 | } 626 | break; 627 | case BITRATE_424KBITS: 628 | { 629 | PCD_WriteRegister(ModWidthReg, 0x0A); 630 | } 631 | break; 632 | case BITRATE_848KBITS: 633 | { 634 | PCD_WriteRegister(ModWidthReg, 0x05); 635 | } 636 | break; 637 | default: 638 | { 639 | PCD_WriteRegister(ModWidthReg, 0x26); // Default value 640 | } 641 | break; 642 | } 643 | 644 | //PCD_WriteRegister(RxThresholdReg, 0x84); // ISO-14443.4 Type A (default) 645 | //PCD_WriteRegister(ControlReg, 0x10); 646 | 647 | delayMicroseconds(10); 648 | } 649 | else 650 | { 651 | return STATUS_ERROR; 652 | } 653 | } 654 | 655 | return result; 656 | } // End PICC_PPS() 657 | 658 | 659 | ///////////////////////////////////////////////////////////////////////////////////// 660 | // Functions for communicating with ISO/IEC 14433-4 cards 661 | ///////////////////////////////////////////////////////////////////////////////////// 662 | 663 | MFRC522::StatusCode MFRC522Extended::TCL_Transceive(PcbBlock *send, PcbBlock *back) 664 | { 665 | MFRC522::StatusCode result; 666 | byte inBuffer[FIFO_SIZE]; 667 | byte inBufferSize = FIFO_SIZE; 668 | byte outBuffer[send->inf.size + 5]; // PCB + CID + NAD + INF + EPILOGUE (CRC) 669 | byte outBufferOffset = 1; 670 | byte inBufferOffset = 1; 671 | 672 | // Set the PCB byte 673 | outBuffer[0] = send->prologue.pcb; 674 | 675 | // Set the CID byte if available 676 | if (send->prologue.pcb & 0x08) { 677 | outBuffer[outBufferOffset] = send->prologue.cid; 678 | outBufferOffset++; 679 | } 680 | 681 | // Set the NAD byte if available 682 | if (send->prologue.pcb & 0x04) { 683 | outBuffer[outBufferOffset] = send->prologue.nad; 684 | outBufferOffset++; 685 | } 686 | 687 | // Copy the INF field if available 688 | if (send->inf.size > 0) { 689 | memcpy(&outBuffer[outBufferOffset], send->inf.data, send->inf.size); 690 | outBufferOffset += send->inf.size; 691 | } 692 | 693 | // Is the CRC enabled for transmission? 694 | byte txModeReg = PCD_ReadRegister(TxModeReg); 695 | if ((txModeReg & 0x80) != 0x80) { 696 | // Calculate CRC_A 697 | result = PCD_CalculateCRC(outBuffer, outBufferOffset, &outBuffer[outBufferOffset]); 698 | if (result != STATUS_OK) { 699 | return result; 700 | } 701 | 702 | outBufferOffset += 2; 703 | } 704 | 705 | // Transceive the block 706 | result = PCD_TransceiveData(outBuffer, outBufferOffset, inBuffer, &inBufferSize); 707 | if (result != STATUS_OK) { 708 | return result; 709 | } 710 | 711 | // We want to turn the received array back to a PcbBlock 712 | back->prologue.pcb = inBuffer[0]; 713 | 714 | // CID byte is present? 715 | if (send->prologue.pcb & 0x08) { 716 | back->prologue.cid = inBuffer[inBufferOffset]; 717 | inBufferOffset++; 718 | } 719 | 720 | // NAD byte is present? 721 | if (send->prologue.pcb & 0x04) { 722 | back->prologue.nad = inBuffer[inBufferOffset]; 723 | inBufferOffset++; 724 | } 725 | 726 | // Check if CRC is taken care of by MFRC522 727 | byte rxModeReg = PCD_ReadRegister(TxModeReg); 728 | if ((rxModeReg & 0x80) != 0x80) { 729 | Serial.print("CRC is not taken care of by MFRC522: "); 730 | Serial.println(rxModeReg, HEX); 731 | 732 | // Check the CRC 733 | // We need at least the CRC_A value. 734 | if ((int)(inBufferSize - inBufferOffset) < 2) { 735 | return STATUS_CRC_WRONG; 736 | } 737 | 738 | // Verify CRC_A - do our own calculation and store the control in controlBuffer. 739 | byte controlBuffer[2]; 740 | MFRC522::StatusCode status = PCD_CalculateCRC(inBuffer, inBufferSize - 2, controlBuffer); 741 | if (status != STATUS_OK) { 742 | return status; 743 | } 744 | 745 | if ((inBuffer[inBufferSize - 2] != controlBuffer[0]) || (inBuffer[inBufferSize - 1] != controlBuffer[1])) { 746 | return STATUS_CRC_WRONG; 747 | } 748 | 749 | // Take away the CRC bytes 750 | inBufferSize -= 2; 751 | } 752 | 753 | // Got more data? 754 | if (inBufferSize > inBufferOffset) { 755 | if ((inBufferSize - inBufferOffset) > back->inf.size) { 756 | return STATUS_NO_ROOM; 757 | } 758 | 759 | memcpy(back->inf.data, &inBuffer[inBufferOffset], inBufferSize - inBufferOffset); 760 | back->inf.size = inBufferSize - inBufferOffset; 761 | } else { 762 | back->inf.size = 0; 763 | } 764 | 765 | // If the response is a R-Block check NACK 766 | if (((inBuffer[0] & 0xC0) == 0x80) && (inBuffer[0] & 0x20)) { 767 | return STATUS_MIFARE_NACK; 768 | } 769 | 770 | return result; 771 | } 772 | /** 773 | * Send an I-Block (Application) 774 | */ 775 | MFRC522::StatusCode MFRC522Extended::TCL_Transceive(TagInfo *tag, byte *sendData, byte sendLen, byte *backData, byte *backLen) 776 | { 777 | MFRC522::StatusCode result; 778 | 779 | PcbBlock out; 780 | PcbBlock in; 781 | byte outBuffer[FIFO_SIZE]; 782 | byte outBufferSize = FIFO_SIZE; 783 | byte totalBackLen = *backLen; 784 | 785 | // This command sends an I-Block 786 | out.prologue.pcb = 0x02; 787 | 788 | if (tag->ats.tc1.supportsCID) { 789 | out.prologue.pcb |= 0x08; 790 | out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 791 | } 792 | 793 | // This command doe not support NAD 794 | out.prologue.pcb &= 0xFB; 795 | out.prologue.nad = 0x00; 796 | 797 | // Set the block number 798 | if (tag->blockNumber) { 799 | out.prologue.pcb |= 0x01; 800 | } 801 | 802 | // Do we have data to send? 803 | if (sendData && (sendLen > 0)) { 804 | out.inf.size = sendLen; 805 | out.inf.data = sendData; 806 | } else { 807 | out.inf.size = 0; 808 | out.inf.data = NULL; 809 | } 810 | 811 | // Initialize the receiving data 812 | // TODO Warning: Value escapes the local scope 813 | in.inf.data = outBuffer; 814 | in.inf.size = outBufferSize; 815 | 816 | result = TCL_Transceive(&out, &in); 817 | if (result != STATUS_OK) { 818 | return result; 819 | } 820 | 821 | // Swap block number on success 822 | tag->blockNumber = !tag->blockNumber; 823 | 824 | if (backData && (backLen > 0)) { 825 | if (*backLen < in.inf.size) 826 | return STATUS_NO_ROOM; 827 | 828 | *backLen = in.inf.size; 829 | memcpy(backData, in.inf.data, in.inf.size); 830 | } 831 | 832 | // Check chaining 833 | if ((in.prologue.pcb & 0x10) == 0x00) 834 | return result; 835 | 836 | // Result is chained 837 | // Send an ACK to receive more data 838 | // TODO: Should be checked I've never needed to send an ACK 839 | while (in.prologue.pcb & 0x10) { 840 | byte ackData[FIFO_SIZE]; 841 | byte ackDataSize = FIFO_SIZE; 842 | 843 | result = TCL_TransceiveRBlock(tag, true, ackData, &ackDataSize); 844 | if (result != STATUS_OK) 845 | return result; 846 | 847 | if (backData && (backLen > 0)) { 848 | if ((*backLen + ackDataSize) > totalBackLen) 849 | return STATUS_NO_ROOM; 850 | 851 | memcpy(&(backData[*backLen]), ackData, ackDataSize); 852 | *backLen += ackDataSize; 853 | } 854 | } 855 | 856 | return result; 857 | } // End TCL_Transceive() 858 | 859 | /** 860 | * Send R-Block to the PICC. 861 | */ 862 | MFRC522::StatusCode MFRC522Extended::TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData, byte *backLen) 863 | { 864 | MFRC522::StatusCode result; 865 | 866 | PcbBlock out; 867 | PcbBlock in; 868 | byte outBuffer[FIFO_SIZE]; 869 | byte outBufferSize = FIFO_SIZE; 870 | 871 | // This command sends an R-Block 872 | if (ack) 873 | out.prologue.pcb = 0xA2; // ACK 874 | else 875 | out.prologue.pcb = 0xB2; // NAK 876 | 877 | 878 | if (tag->ats.tc1.supportsCID) { 879 | out.prologue.pcb |= 0x08; 880 | out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 881 | } 882 | 883 | // This command doe not support NAD 884 | out.prologue.pcb &= 0xFB; 885 | out.prologue.nad = 0x00; 886 | 887 | // Set the block number 888 | if (tag->blockNumber) { 889 | out.prologue.pcb |= 0x01; 890 | } 891 | 892 | // No INF data for R-Block 893 | out.inf.size = 0; 894 | out.inf.data = NULL; 895 | 896 | // Initialize the receiving data 897 | // TODO Warning: Value escapes the local scope 898 | in.inf.data = outBuffer; 899 | in.inf.size = outBufferSize; 900 | 901 | result = TCL_Transceive(&out, &in); 902 | if (result != STATUS_OK) { 903 | return result; 904 | } 905 | 906 | // Swap block number on success 907 | tag->blockNumber = !tag->blockNumber; 908 | 909 | if (backData && backLen) { 910 | if (*backLen < in.inf.size) 911 | return STATUS_NO_ROOM; 912 | 913 | *backLen = in.inf.size; 914 | memcpy(backData, in.inf.data, in.inf.size); 915 | } 916 | 917 | return result; 918 | } // End TCL_TransceiveRBlock() 919 | 920 | /** 921 | * Send an S-Block to deselect the card. 922 | */ 923 | MFRC522::StatusCode MFRC522Extended::TCL_Deselect(TagInfo *tag) 924 | { 925 | MFRC522::StatusCode result; 926 | byte outBuffer[4]; 927 | byte outBufferSize = 1; 928 | byte inBuffer[FIFO_SIZE]; 929 | byte inBufferSize = FIFO_SIZE; 930 | 931 | outBuffer[0] = 0xC2; 932 | if (tag->ats.tc1.supportsCID) 933 | { 934 | outBuffer[0] |= 0x08; 935 | outBuffer[1] = 0x00; // CID is hardcoded 936 | outBufferSize = 2; 937 | } 938 | 939 | result = PCD_TransceiveData(outBuffer, outBufferSize, inBuffer, &inBufferSize); 940 | if (result != STATUS_OK) { 941 | return result; 942 | } 943 | 944 | // TODO:Maybe do some checks? In my test it returns: CA 00 (Same data as I sent to my card) 945 | 946 | return result; 947 | } // End TCL_Deselect() 948 | 949 | ///////////////////////////////////////////////////////////////////////////////////// 950 | // Support functions 951 | ///////////////////////////////////////////////////////////////////////////////////// 952 | 953 | /** 954 | * Get the PICC type. 955 | * 956 | * @return PICC_Type 957 | */ 958 | MFRC522::PICC_Type MFRC522Extended::PICC_GetType(TagInfo *tag ///< The TagInfo returned from PICC_Select(). 959 | ) { 960 | // http://www.nxp.com/documents/application_note/AN10833.pdf 961 | // 3.2 Coding of Select Acknowledge (SAK) 962 | // ignore 8-bit (iso14443 starts with LSBit = bit 1) 963 | // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) 964 | byte sak = tag->uid.sak & 0x7F; 965 | switch (sak) { 966 | case 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete 967 | case 0x09: return PICC_TYPE_MIFARE_MINI; 968 | case 0x08: return PICC_TYPE_MIFARE_1K; 969 | case 0x18: return PICC_TYPE_MIFARE_4K; 970 | case 0x00: return PICC_TYPE_MIFARE_UL; 971 | case 0x10: 972 | case 0x11: return PICC_TYPE_MIFARE_PLUS; 973 | case 0x01: return PICC_TYPE_TNP3XXX; 974 | case 0x20: 975 | if (tag->atqa == 0x0344) 976 | return PICC_TYPE_MIFARE_DESFIRE; 977 | return PICC_TYPE_ISO_14443_4; 978 | case 0x40: return PICC_TYPE_ISO_18092; 979 | default: return PICC_TYPE_UNKNOWN; 980 | } 981 | } // End PICC_GetType() 982 | 983 | /** 984 | * Dumps debug info about the selected PICC to Serial. 985 | * On success the PICC is halted after dumping the data. 986 | * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. 987 | */ 988 | void MFRC522Extended::PICC_DumpToSerial(TagInfo *tag) 989 | { 990 | MIFARE_Key key; 991 | 992 | // Dump UID, SAK and Type 993 | PICC_DumpDetailsToSerial(tag); 994 | 995 | // Dump contents 996 | PICC_Type piccType = MFRC522::PICC_GetType(tag->uid.sak); 997 | switch (piccType) { 998 | case PICC_TYPE_MIFARE_MINI: 999 | case PICC_TYPE_MIFARE_1K: 1000 | case PICC_TYPE_MIFARE_4K: 1001 | // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. 1002 | for (byte i = 0; i < 6; i++) { 1003 | key.keyByte[i] = 0xFF; 1004 | } 1005 | PICC_DumpMifareClassicToSerial(&tag->uid, piccType, &key); 1006 | break; 1007 | 1008 | case PICC_TYPE_MIFARE_UL: 1009 | PICC_DumpMifareUltralightToSerial(); 1010 | break; 1011 | 1012 | case PICC_TYPE_ISO_14443_4: 1013 | case PICC_TYPE_MIFARE_DESFIRE: 1014 | PICC_DumpISO14443_4(tag); 1015 | Serial.println(F("Dumping memory contents not implemented for that PICC type.")); 1016 | break; 1017 | case PICC_TYPE_ISO_18092: 1018 | case PICC_TYPE_MIFARE_PLUS: 1019 | case PICC_TYPE_TNP3XXX: 1020 | Serial.println(F("Dumping memory contents not implemented for that PICC type.")); 1021 | break; 1022 | 1023 | case PICC_TYPE_UNKNOWN: 1024 | case PICC_TYPE_NOT_COMPLETE: 1025 | default: 1026 | break; // No memory dump here 1027 | } 1028 | 1029 | Serial.println(); 1030 | PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. 1031 | } 1032 | 1033 | /** 1034 | * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. 1035 | */ 1036 | void MFRC522Extended::PICC_DumpDetailsToSerial(TagInfo *tag ///< Pointer to TagInfo struct returned from a successful PICC_Select(). 1037 | ) { 1038 | // ATQA 1039 | Serial.print(F("Card ATQA:")); 1040 | if (((tag->atqa & 0xFF00u) >> 8) < 0x10) 1041 | Serial.print(F(" 0")); 1042 | Serial.print((tag->atqa & 0xFF00u) >> 8, HEX); 1043 | if ((tag->atqa & 0x00FFu) < 0x10) 1044 | Serial.print(F("0")); 1045 | else 1046 | Serial.print(F(" ")); 1047 | Serial.println(tag->atqa & 0x00FFu, HEX); 1048 | 1049 | // UID 1050 | Serial.print(F("Card UID:")); 1051 | for (byte i = 0; i < tag->uid.size; i++) { 1052 | if (tag->uid.uidByte[i] < 0x10) 1053 | Serial.print(F(" 0")); 1054 | else 1055 | Serial.print(F(" ")); 1056 | Serial.print(tag->uid.uidByte[i], HEX); 1057 | } 1058 | Serial.println(); 1059 | 1060 | // SAK 1061 | Serial.print(F("Card SAK: ")); 1062 | if (tag->uid.sak < 0x10) 1063 | Serial.print(F("0")); 1064 | Serial.println(tag->uid.sak, HEX); 1065 | 1066 | // (suggested) PICC type 1067 | PICC_Type piccType = PICC_GetType(tag); 1068 | Serial.print(F("PICC type: ")); 1069 | Serial.println(PICC_GetTypeName(piccType)); 1070 | } // End PICC_DumpDetailsToSerial() 1071 | 1072 | /** 1073 | * Dumps memory contents of a ISO-14443-4 PICC. 1074 | */ 1075 | void MFRC522Extended::PICC_DumpISO14443_4(TagInfo *tag) 1076 | { 1077 | // ATS 1078 | if (tag->ats.size > 0x00) { // The first byte is the ATS length including the length byte 1079 | Serial.print(F("Card ATS:")); 1080 | for (byte offset = 0; offset < tag->ats.size; offset++) { 1081 | if (tag->ats.data[offset] < 0x10) 1082 | Serial.print(F(" 0")); 1083 | else 1084 | Serial.print(F(" ")); 1085 | Serial.print(tag->ats.data[offset], HEX); 1086 | } 1087 | Serial.println(); 1088 | } 1089 | 1090 | } // End PICC_DumpISO14443_4 1091 | 1092 | ///////////////////////////////////////////////////////////////////////////////////// 1093 | // Convenience functions - does not add extra functionality 1094 | ///////////////////////////////////////////////////////////////////////////////////// 1095 | 1096 | /** 1097 | * Returns true if a PICC responds to PICC_CMD_REQA. 1098 | * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. 1099 | * 1100 | * @return bool 1101 | */ 1102 | bool MFRC522Extended::PICC_IsNewCardPresent() { 1103 | byte bufferATQA[2]; 1104 | byte bufferSize = sizeof(bufferATQA); 1105 | 1106 | // Reset baud rates 1107 | PCD_WriteRegister(TxModeReg, 0x00); 1108 | PCD_WriteRegister(RxModeReg, 0x00); 1109 | // Reset ModWidthReg 1110 | PCD_WriteRegister(ModWidthReg, 0x26); 1111 | 1112 | MFRC522::StatusCode result = PICC_RequestA(bufferATQA, &bufferSize); 1113 | 1114 | if (result == STATUS_OK || result == STATUS_COLLISION) { 1115 | tag.atqa = ((uint16_t)bufferATQA[1] << 8) | bufferATQA[0]; 1116 | tag.ats.size = 0; 1117 | tag.ats.fsc = 32; // default FSC value 1118 | 1119 | // Defaults for TA1 1120 | tag.ats.ta1.transmitted = false; 1121 | tag.ats.ta1.sameD = false; 1122 | tag.ats.ta1.ds = MFRC522Extended::BITRATE_106KBITS; 1123 | tag.ats.ta1.dr = MFRC522Extended::BITRATE_106KBITS; 1124 | 1125 | // Defaults for TB1 1126 | tag.ats.tb1.transmitted = false; 1127 | tag.ats.tb1.fwi = 0; // TODO: Don't know the default for this! 1128 | tag.ats.tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) 1129 | 1130 | // Defaults for TC1 1131 | tag.ats.tc1.transmitted = false; 1132 | tag.ats.tc1.supportsCID = true; 1133 | tag.ats.tc1.supportsNAD = false; 1134 | 1135 | memset(tag.ats.data, 0, FIFO_SIZE - 2); 1136 | 1137 | tag.blockNumber = false; 1138 | return true; 1139 | } 1140 | return false; 1141 | } // End PICC_IsNewCardPresent() 1142 | 1143 | /** 1144 | * Simple wrapper around PICC_Select. 1145 | * Returns true if a UID could be read. 1146 | * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. 1147 | * The read UID is available in the class variable uid. 1148 | * 1149 | * @return bool 1150 | */ 1151 | bool MFRC522Extended::PICC_ReadCardSerial() { 1152 | MFRC522::StatusCode result = PICC_Select(&tag.uid); 1153 | 1154 | // Backward compatibility 1155 | uid.size = tag.uid.size; 1156 | uid.sak = tag.uid.sak; 1157 | memcpy(uid.uidByte, tag.uid.uidByte, sizeof(tag.uid.uidByte)); 1158 | 1159 | return (result == STATUS_OK); 1160 | } // End 1161 | -------------------------------------------------------------------------------- /MFRC522Extended.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Library extends MFRC522.h to support RATS for ISO-14443-4 PICC. 3 | * RATS - Request for Answer To Select. 4 | * @author JPG-Consulting 5 | */ 6 | #ifndef MFRC522Extended_h 7 | #define MFRC522Extended_h 8 | 9 | #include 10 | #include "MFRC522.h" 11 | 12 | class MFRC522Extended : public MFRC522 { 13 | 14 | public: 15 | // ISO/IEC 14443-4 bit rates 16 | enum TagBitRates : byte { 17 | BITRATE_106KBITS = 0x00, 18 | BITRATE_212KBITS = 0x01, 19 | BITRATE_424KBITS = 0x02, 20 | BITRATE_848KBITS = 0x03 21 | }; 22 | 23 | // Structure to store ISO/IEC 14443-4 ATS 24 | typedef struct { 25 | byte size; 26 | byte fsc; // Frame size for proximity card 27 | 28 | struct { 29 | bool transmitted; 30 | bool sameD; // Only the same D for both directions supported 31 | TagBitRates ds; // Send D 32 | TagBitRates dr; // Receive D 33 | } ta1; 34 | 35 | struct { 36 | bool transmitted; 37 | byte fwi; // Frame waiting time integer 38 | byte sfgi; // Start-up frame guard time integer 39 | } tb1; 40 | 41 | struct { 42 | bool transmitted; 43 | bool supportsCID; 44 | bool supportsNAD; 45 | } tc1; 46 | 47 | // Raw data from ATS 48 | byte data[FIFO_SIZE - 2]; // ATS cannot be bigger than FSD - 2 bytes (CRC), according to ISO 14443-4 5.2.2 49 | } Ats; 50 | 51 | // A struct used for passing the PICC information 52 | typedef struct { 53 | uint16_t atqa; 54 | Uid uid; 55 | Ats ats; 56 | 57 | // For Block PCB 58 | bool blockNumber; 59 | } TagInfo; 60 | 61 | // A struct used for passing PCB Block 62 | typedef struct { 63 | struct { 64 | byte pcb; 65 | byte cid; 66 | byte nad; 67 | } prologue; 68 | struct { 69 | byte size; 70 | byte *data; 71 | } inf; 72 | } PcbBlock; 73 | 74 | // Member variables 75 | TagInfo tag; 76 | 77 | ///////////////////////////////////////////////////////////////////////////////////// 78 | // Contructors 79 | ///////////////////////////////////////////////////////////////////////////////////// 80 | MFRC522Extended() : MFRC522() {}; 81 | MFRC522Extended(uint8_t rst) : MFRC522(rst) {}; 82 | MFRC522Extended(uint8_t ss, uint8_t rst) : MFRC522(ss, rst) {}; 83 | 84 | ///////////////////////////////////////////////////////////////////////////////////// 85 | // Functions for communicating with PICCs 86 | ///////////////////////////////////////////////////////////////////////////////////// 87 | StatusCode PICC_Select(Uid *uid, byte validBits = 0) override; // overrride 88 | StatusCode PICC_RequestATS(Ats *ats); 89 | StatusCode PICC_PPS(); // PPS command without bitrate parameter 90 | StatusCode PICC_PPS(TagBitRates sendBitRate, TagBitRates receiveBitRate); // Different D values 91 | 92 | ///////////////////////////////////////////////////////////////////////////////////// 93 | // Functions for communicating with ISO/IEC 14433-4 cards 94 | ///////////////////////////////////////////////////////////////////////////////////// 95 | StatusCode TCL_Transceive(PcbBlock *send, PcbBlock *back); 96 | StatusCode TCL_Transceive(TagInfo * tag, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL); 97 | StatusCode TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData = NULL, byte *backLen = NULL); 98 | StatusCode TCL_Deselect(TagInfo *tag); 99 | 100 | ///////////////////////////////////////////////////////////////////////////////////// 101 | // Support functions 102 | ///////////////////////////////////////////////////////////////////////////////////// 103 | static PICC_Type PICC_GetType(TagInfo *tag); 104 | using MFRC522::PICC_GetType;// // make old PICC_GetType(byte sak) available, otherwise would be hidden by PICC_GetType(TagInfo *tag) 105 | 106 | // Support functions for debuging 107 | void PICC_DumpToSerial(TagInfo *tag); 108 | using MFRC522::PICC_DumpToSerial; // make old PICC_DumpToSerial(Uid *uid) available, otherwise would be hidden by PICC_DumpToSerial(TagInfo *tag) 109 | void PICC_DumpDetailsToSerial(TagInfo *tag); 110 | using MFRC522::PICC_DumpDetailsToSerial; // make old PICC_DumpDetailsToSerial(Uid *uid) available, otherwise would be hidden by PICC_DumpDetailsToSerial(TagInfo *tag) 111 | void PICC_DumpISO14443_4(TagInfo *tag); 112 | 113 | ///////////////////////////////////////////////////////////////////////////////////// 114 | // Convenience functions - does not add extra functionality 115 | ///////////////////////////////////////////////////////////////////////////////////// 116 | bool PICC_IsNewCardPresent() override; // overrride 117 | bool PICC_ReadCardSerial() override; // overrride 118 | }; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magical Alexa-Spotify-CD Box 2 | 3 | 4 | For Christmas, I signed myself up for the "Creative Engineering with Mark Rober". As part of the class we have to build something with an Arduino. Here is my project. 5 | 6 | For my final project in the Creative Engineering with Mark Rober class, I submit to you my magic Alexa-Spotify-CD box. 7 | I just don't have the heart to throw out all my old CDs and thought this project might give them a second life. I took an 8 | esp32 microcontroller, hooked a RFID-RC522 reader to it, and wrote some code to read a Spotify link from NFC tags that I 9 | place on the CD cases and make a API call to Spotify to play that album on the Echo Dot that I mounted into a Walnut and 10 | Birdseye Maple case that I built. 11 | 12 | 13 | See my YouTube video for more information. 14 | 15 | ## Magical Spotify Player 16 | 17 | [![How to Build a Cool Spotify Player](https://img.youtube.com/vi/H_9H8qFnDr8/0.jpg)](https://youtu.be/H_9H8qFnDr8 "How to Build a Cool Spotify Player") 18 | 19 | 20 | ## How to use Spotify API on ESP32 with NFC Reader to control Echo Dot 21 | 22 | [![Code walk thru](https://img.youtube.com/vi/RMtRH-3sTR4/0.jpg)](https://youtu.be/RMtRH-3sTR4 "Code walk thru") 23 | 24 | ## Magical Alexa-Spotify-CD Box 25 | 26 | [![Magical Alexa-Spotify-CD Box](https://img.youtube.com/vi/H2HJ-LY7-lQ/0.jpg)](https://youtu.be/H2HJ-LY7-lQ "Magical Alexa-Spotify-CD Box") 27 | 28 | ## My Project for Creative Engineering with Mark Rober | The Idea 29 | 30 | [![My Project for Creative Engineering with Mark Rober | The Idea](https://img.youtube.com/vi/7CPkmOHev_A/0.jpg)](https://youtu.be/7CPkmOHev_A "My Project for Creative Engineering with Mark Rober | The Idea") 31 | 32 | 33 | ## Progress 34 | 35 | [![Progress](https://img.youtube.com/vi/dpDbMA8f0VY/0.jpg)](https://youtu.be//dpDbMA8f0VY "Progress") 36 | 37 | ## It's working 38 | 39 | [![It's working](https://img.youtube.com/vi/isom4NREq14/0.jpg)](https://youtu.be/isom4NREq14 "It's working") 40 | 41 | 42 | And checkout my YouTube channel for other maker projects I have done. https://www.youtube.com/c/MakerAtPlay 43 | -------------------------------------------------------------------------------- /SpotifyClient.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define CORE_DEBUG_LEVEL ARDUHAL_LOG_LEVEL_VERBOSE 4 | 5 | #include 6 | #include "SpotifyClient.h" 7 | #include 8 | 9 | 10 | SpotifyClient::SpotifyClient(String clientId, String clientSecret, String deviceName, String refreshToken) { 11 | this->clientId = clientId; 12 | this->clientSecret = clientSecret; 13 | this->refreshToken = refreshToken; 14 | this->deviceName = deviceName; 15 | 16 | client.setCACert(digicert_root_ca); 17 | } 18 | 19 | void SpotifyClient::FetchToken() 20 | { 21 | HTTPClient http; 22 | 23 | String body = "grant_type=refresh_token&refresh_token=" + refreshToken; 24 | String authorizationRaw = clientId + ":" + clientSecret; 25 | String authorization = base64::encode(authorizationRaw); 26 | http.begin(client, "https://accounts.spotify.com/api/token"); 27 | http.addHeader("Content-Type", "application/x-www-form-urlencoded"); 28 | http.addHeader("Authorization", "Basic " + authorization); 29 | 30 | int httpCode = http.POST(body); 31 | if (httpCode > 0) { 32 | String returnedPayload = http.getString(); 33 | if ( httpCode == 200 ) 34 | { 35 | accessToken = ParseJson("access_token", returnedPayload ); 36 | Serial.println("Got new access token"); 37 | } 38 | else 39 | { 40 | Serial.println("Failed to get new access token"); 41 | Serial.println(httpCode); 42 | Serial.println(returnedPayload); 43 | } 44 | } 45 | else { 46 | Serial.println("Failed to connect to https://accounts.spotify.com/api/token" ); 47 | } 48 | http.end(); 49 | } 50 | 51 | int SpotifyClient::Shuffle() 52 | { 53 | Serial.println("Shuffle()"); 54 | HttpResult result = CallAPI( "PUT", "https://api.spotify.com/v1/me/player/shuffle?state=true&device_id=" + deviceId, "" ); 55 | return result.httpCode; 56 | } 57 | 58 | int SpotifyClient::Next() 59 | { 60 | Serial.println("Next()"); 61 | HttpResult result = CallAPI( "POST", "https://api.spotify.com/v1/me/player/next?device_id=" + deviceId, "" ); 62 | return result.httpCode; 63 | } 64 | 65 | 66 | int SpotifyClient::Play(String context_uri ) 67 | { 68 | Serial.println("Play()"); 69 | String body = "{\"context_uri\":\"" + context_uri + "\",\"offset\":{\"position\":0,\"position_ms\":0}}"; 70 | String url = "https://api.spotify.com/v1/me/player/play?device_id=" + deviceId; 71 | 72 | HttpResult result = CallAPI( "PUT", url, body ); 73 | return result.httpCode; 74 | } 75 | 76 | HttpResult SpotifyClient::CallAPI( String method, String url, String body ) 77 | { 78 | HttpResult result; 79 | result.httpCode = 0; 80 | Serial.print(url); 81 | Serial.print( " returned: " ); 82 | 83 | HTTPClient http; 84 | http.begin(client, url); 85 | 86 | String authorization = "Bearer " + accessToken; 87 | 88 | http.addHeader(F("Content-Type"), "application/json"); 89 | http.addHeader(F("Authorization"), authorization); 90 | 91 | // address bug where Content-Length not added by HTTPClient is content length is zero 92 | if ( body.length() == 0 ) 93 | { 94 | http.addHeader(F("Content-Length"), String(0)); 95 | } 96 | 97 | if ( method == "PUT" ) 98 | { 99 | result.httpCode = http.PUT(body); 100 | } 101 | else if ( method == "POST" ) 102 | { 103 | result.httpCode = http.POST(body); 104 | } 105 | else if ( method == "GET" ) 106 | { 107 | result.httpCode = http.GET(); 108 | } 109 | 110 | if (result.httpCode > 0) 111 | { 112 | Serial.println(result.httpCode); 113 | if ( http.getSize() > 0 ) 114 | { 115 | result.payload = http.getString(); 116 | } 117 | } 118 | else 119 | { 120 | Serial.print("Failed to connect to "); 121 | Serial.println(url); 122 | } 123 | http.end(); 124 | 125 | return result; 126 | } 127 | 128 | /* 129 | int SpotifyClient::CallAPI( String method, String url, String body ) 130 | { 131 | HTTPClient http; 132 | http.begin(client, url); 133 | http.addHeader("Content-Type", "application/json"); 134 | String authorization = "Bearer " + accessToken; 135 | http.addHeader("Authorization", authorization); 136 | 137 | int httpCode = 0; 138 | if ( method == "PUT" ) 139 | { 140 | httpCode = http.PUT(body); 141 | } 142 | else if ( method == "POST" ) 143 | { 144 | httpCode = http.POST(body); 145 | } 146 | else if ( method == "GET" ) 147 | { 148 | httpCode = http.GET(); 149 | } 150 | if (httpCode > 0) { //Check for the returning code 151 | String returnedPayload = http.getString(); 152 | if ( httpCode == 200 ) 153 | { 154 | accessToken = ParseJson("access_token", returnedPayload ); 155 | Serial.println("Got new access token"); 156 | } 157 | else 158 | { 159 | Serial.print("API call returned error: "); 160 | Serial.println(httpCode); 161 | Serial.println(returnedPayload); 162 | } 163 | } 164 | else { 165 | Serial.print("Failed to connect to "); 166 | Serial.println(url); 167 | } 168 | http.end(); //Free the resources 169 | 170 | return httpCode; 171 | } 172 | */ 173 | 174 | String SpotifyClient::GetDevices() 175 | { 176 | HttpResult result = CallAPI( "GET", "https://api.spotify.com/v1/me/player/devices", "" ); 177 | deviceId = GetDeviceId(result.payload); 178 | Serial.print("Device ID: "); 179 | Serial.println(deviceId); 180 | return deviceId; 181 | } 182 | 183 | 184 | String SpotifyClient::GetDeviceId(String json) 185 | { 186 | String id = ""; 187 | 188 | // find the position the device name 189 | int index = json.indexOf(deviceName); 190 | if ( index > 0 ) 191 | { 192 | // we found it, so not backup to begining of this object 193 | int i = index; 194 | for (; i > 0; i-- ) 195 | { 196 | if ( json.charAt(i) == '{') 197 | { 198 | break; 199 | } 200 | } 201 | 202 | // now move forward and find "id" 203 | for (; i < json.length(); i++ ) 204 | { 205 | if ( json.charAt(i) == '}') 206 | { 207 | break; 208 | } 209 | 210 | if ( i + 3 < json.length() && json.charAt(i) == '"' 211 | && json.charAt(i+1) == 'i' && json.charAt(i+2) == 'd' && json.charAt(i+3) == '"') 212 | { 213 | i += 4; 214 | break; 215 | } 216 | } 217 | 218 | // move forward to next " 219 | for (; i < json.length() ; i++ ){ 220 | if ( json.charAt(i) == '"'){ 221 | i++; 222 | break; 223 | } 224 | } 225 | 226 | // now get that device id 227 | for (; i < json.length() ; i++ ){ 228 | if ( json.charAt(i) != '"' ) 229 | { 230 | id += json.charAt(i); 231 | } 232 | else 233 | { 234 | break; 235 | } 236 | 237 | } 238 | } 239 | else 240 | { 241 | Serial.print(deviceName); 242 | Serial.println( " device name not found."); 243 | } 244 | return id; 245 | } 246 | 247 | String SpotifyClient::ParseJson(String key, String json ) 248 | { 249 | String retVal = ""; 250 | int index = json.indexOf(key); 251 | 252 | //Serial.print( "index: " ); 253 | //Serial.println(index); 254 | if ( index > 0 ) 255 | { 256 | bool copy = false; 257 | for ( int i = index; i < json.length(); i++ ) 258 | { 259 | //Serial.print( "charAt: " ); 260 | //Serial.println( json.charAt(i) ); 261 | if ( copy ) 262 | { 263 | if ( json.charAt(i) == '"' || json.charAt(i) == ',') 264 | { 265 | break; 266 | } 267 | else 268 | { 269 | retVal += json.charAt(i); 270 | } 271 | } 272 | else if ( json.charAt(i) == ':') 273 | { 274 | copy = true; 275 | if ( json.charAt(i+1) == '"' ) 276 | { 277 | i++; 278 | } 279 | } 280 | } 281 | } 282 | return retVal; 283 | } -------------------------------------------------------------------------------- /SpotifyClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct HttpResult{ 6 | int httpCode; 7 | String payload; 8 | }; 9 | 10 | class SpotifyClient { 11 | public: 12 | SpotifyClient(String clientId, String clientSecret, String deviceName, String refreshToken); 13 | 14 | void FetchToken(); 15 | int Play( String context_uri ); 16 | int Shuffle(); 17 | int Next(); 18 | String GetDevices(); 19 | 20 | private: 21 | WiFiClientSecure client; 22 | String clientId; 23 | String clientSecret; 24 | String redirectUri; 25 | String accessToken; 26 | String refreshToken; 27 | String deviceId; 28 | String deviceName; 29 | 30 | String ParseJson(String key, String json ); 31 | HttpResult CallAPI( String method, String url, String body ); 32 | 33 | String GetDeviceId(String json); 34 | 35 | // root certificate for spotify.com 36 | const char* digicert_root_ca= \ 37 | "-----BEGIN CERTIFICATE-----\n" \ 38 | "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" \ 39 | "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" \ 40 | "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" \ 41 | "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" \ 42 | "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" \ 43 | "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" \ 44 | "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" \ 45 | "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" \ 46 | "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" \ 47 | "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" \ 48 | "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" \ 49 | "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" \ 50 | "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" \ 51 | "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" \ 52 | "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" \ 53 | "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" \ 54 | "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" \ 55 | "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" \ 56 | "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" \ 57 | "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" \ 58 | "-----END CERTIFICATE-----\n"; 59 | }; -------------------------------------------------------------------------------- /deprecated.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 by Ludwig Grill (www.rotzbua.de) 3 | * Simple deprecated workaround for Arduino IDE 4 | * IDE 1.6.8 use gcc 4.8 which do not support c++14 [[deprecated]] 5 | * Later versions should support c++14, then use c++14 syntax 6 | */ 7 | #ifndef DEPRECATED_H 8 | #define DEPRECATED_H 9 | 10 | #ifdef __has_cpp_attribute 11 | #if __has_cpp_attribute(deprecated) 12 | #define DEPRECATED [[deprecated]] 13 | #define DEPRECATED_MSG(msg) [[deprecated(msg)]] 14 | #endif // __has_cpp_attribute(deprecated) 15 | #else 16 | #define DEPRECATED __attribute__((deprecated)) 17 | #define DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) 18 | #endif // __has_cpp_attribute 19 | 20 | #endif // DEPRECATED_H 21 | -------------------------------------------------------------------------------- /esp32SpotifyEchoDot.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "SpotifyClient.h" 4 | 5 | #include 6 | #include "MFRC522.h" 7 | 8 | #include "settings.h" 9 | 10 | #define RST_PIN 22 // Configurable, see typical pin layout above 11 | #define SS_PIN 21 // Configurable, see typical pin layout above 12 | 13 | MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance 14 | 15 | byte const BUFFERSiZE = 176; 16 | 17 | 18 | SpotifyClient spotify = SpotifyClient(clientId, clientSecret, deviceName, refreshToken); 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println("Setup started"); 24 | 25 | connectWifi(); 26 | 27 | // Init SPI bus and MFRC522 for NFC reader 28 | SPI.begin(); 29 | mfrc522.PCD_Init(); 30 | 31 | // Refresh Spotify Auth token and Deivce ID 32 | spotify.FetchToken(); 33 | spotify.GetDevices(); 34 | } 35 | 36 | void loop() 37 | { 38 | if (mfrc522.PICC_IsNewCardPresent()) 39 | { 40 | Serial.println("NFC tag present"); 41 | readNFCTag(); 42 | } 43 | } 44 | 45 | void readNFCTag() 46 | { 47 | if (mfrc522.PICC_ReadCardSerial()) 48 | { 49 | byte dataBuffer[BUFFERSiZE]; 50 | readNFCTagData(dataBuffer); 51 | mfrc522.PICC_HaltA(); 52 | 53 | //hexDump(dataBuffer); 54 | Serial.print("Read NFC tag: "); 55 | String context_uri = parseNFCTagData(dataBuffer); 56 | Serial.println(context_uri); 57 | playSpotifyUri(context_uri); 58 | } 59 | } 60 | 61 | void playSpotifyUri(String context_uri) 62 | { 63 | int code = spotify.Play(context_uri); 64 | switch (code) 65 | { 66 | case 404: 67 | { 68 | // device id changed, get new one 69 | spotify.GetDevices(); 70 | spotify.Play(context_uri); 71 | spotify.Shuffle(); 72 | break; 73 | } 74 | case 401: 75 | { 76 | // auth token expired, get new one 77 | spotify.FetchToken(); 78 | spotify.Play(context_uri); 79 | spotify.Shuffle(); 80 | break; 81 | } 82 | default: 83 | { 84 | spotify.Shuffle(); 85 | break; 86 | } 87 | } 88 | } 89 | 90 | bool readNFCTagData(byte *dataBuffer) 91 | { 92 | MFRC522::StatusCode status; 93 | byte byteCount; 94 | byte buffer[18]; 95 | byte x = 0; 96 | 97 | int totalBytesRead = 0; 98 | 99 | // reset the dataBuffer 100 | for (byte i = 0; i < BUFFERSiZE; i++) 101 | { 102 | dataBuffer[i] = 0; 103 | } 104 | 105 | for (byte page = 0; page < BUFFERSiZE / 4; page += 4) 106 | { 107 | // Read pages 108 | byteCount = sizeof(buffer); 109 | status = mfrc522.MIFARE_Read(page, buffer, &byteCount); 110 | if (status == mfrc522.STATUS_OK) 111 | { 112 | totalBytesRead += byteCount - 2; 113 | 114 | for (byte i = 0; i < byteCount - 2; i++) 115 | { 116 | dataBuffer[x++] = buffer[i]; // add data output buffer 117 | } 118 | } 119 | else 120 | { 121 | break; 122 | } 123 | } 124 | } 125 | 126 | /* 127 | Parse the Spotify link from the NFC tag data 128 | The first 28 bytes from the tag is a header info for the tag 129 | Spotify link starts at position 29 130 | 131 | 132 | Parse a link 133 | open.spotify.com/album/3JfSxDfmwS5OeHPwLSkrfr 134 | open.spotify.com/playlist/69pYSDt6QWuBMtIWSZ8uQb 135 | open.spotify.com/artist/53XhwfbYqKCa1cC15pYq2q 136 | 137 | 138 | Return a uri 139 | spotify:album:3JfSxDfmwS5OeHPwLSkrfr 140 | spotify:playlist:69pYSDt6QWuBMtIWSZ8uQb 141 | spotify:artist:53XhwfbYqKCa1cC15pYq2q 142 | 143 | */ 144 | 145 | String parseNFCTagData(byte *dataBuffer) 146 | { 147 | // first 28 bytes is header info 148 | // data ends with 0xFE 149 | String retVal = "spotify:"; 150 | for (int i = 28 + 17; i < BUFFERSiZE; i++) 151 | { 152 | if (dataBuffer[i] == 0xFE || dataBuffer[i] == 0x00) 153 | { 154 | break; 155 | } 156 | if (dataBuffer[i] == '/') 157 | { 158 | retVal += ':'; 159 | } 160 | else 161 | { 162 | retVal += (char)dataBuffer[i]; 163 | } 164 | } 165 | return retVal; 166 | } 167 | 168 | void hexDump(byte *dataBuffer) 169 | { 170 | Serial.print(F("hexDump: ")); 171 | Serial.println(); 172 | for (byte index = 0; index < BUFFERSiZE; index++) 173 | { 174 | if (index % 4 == 0) 175 | { 176 | Serial.println(); 177 | } 178 | Serial.print(dataBuffer[index] < 0x10 ? F(" 0") : F(" ")); 179 | Serial.print(dataBuffer[index], HEX); 180 | } 181 | } 182 | 183 | void connectWifi() 184 | { 185 | WiFi.begin(ssid, pass); 186 | Serial.println(""); 187 | 188 | // Wait for connection 189 | while (WiFi.status() != WL_CONNECTED) 190 | { 191 | delay(500); 192 | Serial.print("."); 193 | } 194 | Serial.println(""); 195 | Serial.print("Connected to "); 196 | Serial.println(ssid); 197 | Serial.print("IP address: "); 198 | Serial.println(WiFi.localIP()); 199 | } -------------------------------------------------------------------------------- /require_cpp11.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 by Ludwig Grill (www.rotzbua.de) 3 | * Throws error if c++11 is not supported 4 | */ 5 | #ifndef REQUIRE_CPP11_H 6 | #define REQUIRE_CPP11_H 7 | 8 | #if __cplusplus < 201103L 9 | #error "This library needs at least a C++11 compliant compiler, maybe compiler argument for C++11 support is missing or if you use Arduino IDE upgrade to version >=1.6.6" 10 | #endif 11 | 12 | #endif // REQUIRE_CPP11_H 13 | -------------------------------------------------------------------------------- /settings.h: -------------------------------------------------------------------------------- 1 | // WiFi settings 2 | const char* ssid = ""; 3 | const char* pass = ""; 4 | 5 | // Spotify settings 6 | String deviceName = ""; 7 | String clientId = ""; 8 | String clientSecret = ""; 9 | String refreshToken = ""; 10 | 11 | --------------------------------------------------------------------------------