miguelbalboa / rfid

Arduino RFID Library for MFRC522

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Power down modes?

ivoras opened this issue · comments

Hello!

Reading the MFRC522 data sheet, it seems like it supports several power-saving modes, including a "soft power-down" mode (http://www.nxp.com/documents/data_sheet/MFRC522.pdf). Is there a way to use this functionality from this RFID library?

Or, generally, for battery operation, could you document some best practices for saving power when using this library?

Thanks!

Not implemented yet. I will be happy to get a pull request from you :)

commented

Well, I was also reading it but I haven't noticed any useful difference between soft power-down and hard power-down. (?)

And going to hard power-down is quite easy, I just set the RST pin low. To wake it up I call MFRC522.PCD_Init() and it sets RST pin high. You can also find that in PCD_Init() there is 50 milliseconds delay for crystal startup. I modified it for myself to only 5 ms and it seems to work well in my case.

And generally, for battery operation, right now I'm testing the mode that ATmega328 asks MFRC522 if there is any PICC, if not then ATmega sets MFRC's RST pin low and goes to sleep for a second itself. After a second ATmega wakes up, sets RST pin high, asks MFRC and then it all go to sleep again... (Of course if there is a PICC present then it is more complicated.)

And to be woken for as short time as possible I also noticed that in library there is set 25 ms timeout in TReloadReg registers. You can find it in PCD_Init(). This timeout is used in PCD_CommunicateWithPICC(). I shortened it to 10 ms and it seems to work. But like the wakeup delay I don't know what the right minimum is.

commented

I'll answer to myself. I started scope and it seems that my crystal+MFRC seems to be woken up in less than 300 microseconds.

Good to hear about your tests!
Did you measure power consumption difference between soft power-down and hard power-down? What was it?

commented

Well, I'm not testing soft power down. Because like I said, I don't understand what is on or off in this mode.

In hard power-down (RST connected to GND) I measured 0.3 mA. It is quite a lot. I'm using a module called RFID-RC522 (popular and cheap on ebay, you can google it). I previously unsoldered resistor R1 nearby the power LED D1. (I think it was 1 kOhm, I didn't want to unsolder the LED.) But now I can see that there is also 10 kOhm R2 between 3.3V and RST and that makes that 0.3 mA.

So I unsoldered R2 and in hard power-down my cheap multimeter displays 0.1 uA. It is suspiciously much less than "max. 5 microamp" in datasheet.

Btw. two posts before I mentioned 10 ms timeout in PCD_CommunicateWithPICC. I googled datasheet for MF1S503x (MIFARE Classic 1K) and there it looks like that 10 ms is the maximum timeout for he longest operation.

I'm trying to make something like "PICC_IsAnyCardPresent()" and to have this as fast as possible.
Right now I have it this way:

  ...
  mfrc522.PCD_Init();
  byte bufferATQA[2];
  byte bufferSize = sizeof(bufferATQA);
  MFRC522::StatusCode sta = mfrc522.PICC_RequestA(bufferATQA, &bufferSize);
  bool result = (sta == MFRC522::STATUS_OK || sta == MFRC522::STATUS_COLLISION);
  if (!result) {
    sta = mfrc522.PICC_WakeupA(bufferATQA, &bufferSize);
    result = (sta == MFRC522::STATUS_OK || sta == MFRC522::STATUS_COLLISION);
  }
  if (!result) digitalWrite(MFRC522_RST_PIN, LOW);
  ...

This test takes about 30-40 ms according to cpu speed (with shorter wakeup delay, shorter timeouts, faster SPI).

Well I power down both mcu (328p) and the the rc522 exit low power when card is detected.
Still running on 4xAA batteries 6 months so far its a electronic safe hack rfid unlock.
Hardware race conditions can be tricky.

mfrc522.PCD_ReducedPowerCardDetection(MCU, LPMCU_PCD_MS, TH, FW_V);
Only Atmel AVR mcu support for now.

example

/**

 * ----------------------------------------------------------------------------

 * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid

 * for further details and other examples.

 * 

 * NOTE: The library file MFRC522.h has a lot of useful info. Please read it.

 * 

 * Released into the public domain.

 * ----------------------------------------------------------------------------

 * This sample shows how to read and write data blocks on a MIFARE Classic PICC

 * (= card/tag).

 * 

 * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7).

 * 

 * 

 * Typical pin layout used:

 * -----------------------------------------------------------------------------------------

 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino

 *             Reader/PCD   Uno/101       Mega      Nano v3    Leonardo/Micro   Pro Micro

 * Signal      Pin          Pin           Pin       Pin        Pin              Pin

 * -----------------------------------------------------------------------------------------

 * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST

 * SPI SS      SDA(SS)      10            53        D10        10               10

 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16

 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14

 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15

 * 

 */



#include <SPI.h>

#include <MFRC522.h>



#define RST_PIN         9           // Configurable, see typical pin layout above

#define SS_PIN          10          // Configurable, see typical pin layout above



MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance.



MFRC522::MIFARE_Key key;



/**

 * Initialize.

 */

void setup() {

    Serial.begin(9600); // Initialize serial communications with the PC

    while (!Serial);    // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)

    SPI.begin();        // Init SPI bus

    mfrc522.PCD_Init(); // Init MFRC522 card



    // Prepare the key (used both as key A and as key B)

    // using FFFFFFFFFFFFh which is the default at chip delivery from the factory

    for (byte i = 0; i < 6; i++) {

        key.keyByte[i] = 0xFF;

    }



    Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write."));

    Serial.print(F("Using key (for A and B):"));

    dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);

    Serial.println();

    

    Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1"));

}



/**

 * Main loop.

 */

void loop() {

    // Look for new cards low power PCD and MCU etc...

    mfrc522.PCD_ReducedPowerCardDetection(MCU, LPMCU_PCD_MS, TH, FW_V);

    // Select one of the cards

    if ( ! mfrc522.PICC_ReadCardSerial())

        return;



    // Show some details of the PICC (that is: the tag/card)

    Serial.print(F("Card UID:"));

    dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);

    Serial.println();

    Serial.print(F("PICC type: "));

    MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);

    Serial.println(mfrc522.PICC_GetTypeName(piccType));



    // Check for compatibility

    if (    piccType != MFRC522::PICC_TYPE_MIFARE_MINI

        &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K

        &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {

        Serial.println(F("This sample only works with MIFARE Classic cards."));

        return;

    }



    // In this sample we use the second sector,

    // that is: sector #1, covering block #4 up to and including block #7

    byte sector         = 1;

    byte blockAddr      = 4;

    byte dataBlock[]    = {

        0x01, 0x02, 0x03, 0x04, //  1,  2,   3,  4,

        0x05, 0x06, 0x07, 0x08, //  5,  6,   7,  8,

        0x08, 0x09, 0xff, 0x0b, //  9, 10, 255, 12,

        0x0c, 0x0d, 0x0e, 0x0f  // 13, 14,  15, 16

    };

    byte trailerBlock   = 7;

    MFRC522::StatusCode status;

    byte buffer[18];

    byte size = sizeof(buffer);



    // Authenticate using key A

    Serial.println(F("Authenticating using key A..."));

    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));

    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("PCD_Authenticate() failed: "));

        Serial.println(mfrc522.GetStatusCodeName(status));

        return;

    }



    // Show the whole sector as it currently is

    Serial.println(F("Current data in sector:"));

    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);

    Serial.println();



    // Read data from the block

    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);

    Serial.println(F(" ..."));

    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);

    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("MIFARE_Read() failed: "));

        Serial.println(mfrc522.GetStatusCodeName(status));

    }

    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));

    dump_byte_array(buffer, 16); Serial.println();

    Serial.println();



    // Authenticate using key B

    Serial.println(F("Authenticating again using key B..."));

    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));

    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("PCD_Authenticate() failed: "));

        Serial.println(mfrc522.GetStatusCodeName(status));

        return;

    }



    // Write data to the block

    Serial.print(F("Writing data into block ")); Serial.print(blockAddr);

    Serial.println(F(" ..."));

    dump_byte_array(dataBlock, 16); Serial.println();

    status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);

    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("MIFARE_Write() failed: "));

        Serial.println(mfrc522.GetStatusCodeName(status));

    }

    Serial.println();



    // Read data from the block (again, should now be what we have written)

    Serial.print(F("Reading data from block ")); Serial.print(blockAddr);

    Serial.println(F(" ..."));

    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);

    if (status != MFRC522::STATUS_OK) {

        Serial.print(F("MIFARE_Read() failed: "));

        Serial.println(mfrc522.GetStatusCodeName(status));

    }

    Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));

    dump_byte_array(buffer, 16); Serial.println();

        

    // Check that data in block is what we have written

    // by counting the number of bytes that are equal

    Serial.println(F("Checking result..."));

    byte count = 0;

    for (byte i = 0; i < 16; i++) {

        // Compare buffer (= what we've read) with dataBlock (= what we've written)

        if (buffer[i] == dataBlock[i])

            count++;

    }

    Serial.print(F("Number of bytes that match = ")); Serial.println(count);

    if (count == 16) {

        Serial.println(F("Success :-)"));

    } else {

        Serial.println(F("Failure, no match :-("));

        Serial.println(F("  perhaps the write didn't work properly..."));

    }

    Serial.println();

        

    // Dump the sector data

    Serial.println(F("Current data in sector:"));

    mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);

    Serial.println();



    // Halt PICC

    mfrc522.PICC_HaltA();

    // Stop encryption on PCD

    mfrc522.PCD_StopCrypto1();

}



/**

 * Helper routine to dump a byte array as hex values to Serial.

 */

void dump_byte_array(byte *buffer, byte bufferSize) {

    for (byte i = 0; i < bufferSize; i++) {

        Serial.print(buffer[i] < 0x10 ? " 0" : " ");

        Serial.print(buffer[i], HEX);

    }

}
commented

@KarlSix I'm quite interested how the mfrc522.PCD_ReducedPowerCardDetection looks like. Is it please possible to find it somewhere?

Nope I haven't posted the code.

When measuring with oscilloscope, I'm getting exactly 30 ms cycle with soft power down.
The pick (during 30 ms cycle) power consumption is ~20 mA. On power down I get 1.4 mA (note that I did not yet remove the resistors and hoping LED and 10K resistor are eating most of the power here)
here is what I did:

void loop() {
// exit soft power off
	mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange);
	delay(5);  // can go without delay?
	if (!mfrc522.PICC_IsNewCardPresent())
	{
// soft power off
		mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
		delay(50);
		return;
	}

	bool status = mfrc522.PICC_ReadCardSerial();
	if (!status) {
		return;
	}
	mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);

The results are very similar to jk987. However this does not require changing delays in the library (b/c no init is used in the loop)

update: changed the PCD_WriteRegister to PCD_WriteRegister(TPrescalerReg, 0x43) as @jk987 suggested. Now the whole power on cycle is less than 15 mSec. If my estimates are correct and ESP8266 wont' add a lot, sleeping for one second and being active for 15 mSec should translate to 4-6 months on 2 AA batteries

1.4mA is still a lot when battery powered, with arduino mini pro and rc522 total current consumption during sleep should be 60uA.

agree. Need to solder out R1 and R2 and hope to get it down to 100uA or below

commented

@KarlSix Karl, I still would like to ask you to show us your mfrc522.PCD_ReducedPowerCardDetection. Please, is it possible or is it some secret?

No secret for avr based microcontrollers it's simple you guys will figure it out.

Here is the latest from my project
Got rid of ESP-12E as wake up from sleep takes ~300 ms. Decided to go with Arduino Pro Mini (aliexpress clone). ESP-01 is used to transmit RFID tags to a server (I’m basically building an RFID control for SONOS: my 3 years old is using bunch of tags with stickers to switch between his albums).
After switching to 8Mhz Pro Mini the wake cycle became closer to 16-15 msec. Average power consumption (arduino plus RFID) is around 1.5 mA (with sleep period not contributing much as it only adds ~50 uA). Changing RFID-522 initialization helped to reduce the wake cycle to 4 msec. Namely the following proved to be working fine:

	mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x43);		// 10μs.

	mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);		// Reload timer with 0x01E = 30, ie 0.3ms before timeout.
	mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

with reload timer set to 30 us, both card detection and reading the card tag are working fine and the cycle is reduced to 4 msec. For whatever reason further reducing the timer does not have visible effect on the timing.
Out of curiosity I changed the timer to 3 cycles – the card detection still works, but reading the ID does not. Changing it to 1 stops the detection. This makes me think, one can use very aggressive (5?) timer counter to just do the card detection and if detected, reinit the mfrc522 with normal timers and read the card normally. After done, put aggressive settings back. For now I decided not to bother and just use the code below as it looks to work just fine for all my tags. It's running now for 24+ hours on 2 AA batteries and the voltage is stable at 3033 mV. By reading more about ESP-01 I realized that it will stop working long before the batteries are empty (expect ESP-01 to break at ~3V?). To overcome this I ordered a step up 3.3 V regulator plus MOSFET to switch the regulator and ESP on for transmission.
The code is below:

	void mfrc522_fast_Reset()
	{
		digitalWrite(RST_PIN, HIGH);
		mfrc522.PCD_Reset();
		mfrc522.PCD_WriteRegister(mfrc522.TModeReg, 0x80);			// TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
		mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x43);		// 10µs.

		mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);		// Reload timer with 0x01E = 30, ie 0.3ms before timeout.
		mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
		mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x5);

		mfrc522.PCD_WriteRegister(mfrc522.TxASKReg, 0x40);		// Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
		mfrc522.PCD_WriteRegister(mfrc522.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)

		mfrc522.PCD_AntennaOn();						// Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
	}

	void setup() {
		Serial.begin(115200);		// Initialize serial communications with the PC
		while (!Serial) delay(1);		// Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)

		pinMode(ESP_ENABLE_PIN, OUTPUT);
		pinMode(RST_PIN, OUTPUT);
		pinMode(SS_PIN, OUTPUT);
		pinMode(LED_PIN, OUTPUT);

		// init reader
		SPI.begin();			// Init SPI bus
		//SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));

		digitalWrite(SS_PIN, HIGH);
		digitalWrite(RST_PIN, HIGH);

		mfrc522_fast_Reset();
		if (mfrc522.PICC_IsNewCardPresent())
		{
			Serial.println("Initializing ESP-01");
			// Init wifi network
			sendtoESP("i", "");	// start AP and the web to reconfigure WiFi
			delay(120000);		// allow 2 min to reconfigure WiFi
		}

	}

	long readVcc() {
		// Read 1.1V reference against AVcc
		// set the reference to Vcc and the measurement to the internal 1.1V reference
	#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
		ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
	#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
		ADMUX = _BV(MUX5) | _BV(MUX0);
	#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
		ADMUX = _BV(MUX3) | _BV(MUX2);
	#else
		ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
	#endif  

		delay(2); // Wait for Vref to settle
		ADCSRA |= _BV(ADSC); // Start conversion
		while (bit_is_set(ADCSRA, ADSC)); // measuring

		uint8_t low = ADCL; // must read ADCL first - it then locks ADCH  
		uint8_t high = ADCH; // unlocks both

		long result = (high << 8) | low;

		result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
		return result; // Vcc in millivolts
	}

	String measurepower()
	{
		return String(readVcc());
	}

	bool mfrc522_fastDetect()
	{
		byte bufferATQA[2];
		byte bufferSize = sizeof(bufferATQA);
		MFRC522::StatusCode sta = mfrc522.PICC_RequestA(bufferATQA, &bufferSize);
		return (sta == MFRC522::STATUS_OK || sta == MFRC522::STATUS_COLLISION);
	}

	void loop() {
		ticks++;

		// the PCD_NoCmdChange not really needed as the reader is wakened up by the card
		//mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange);

		// Look for new cards
		if(!mfrc522_fastDetect()){
		//if (!mfrc522.PICC_IsNewCardPresent()) {
			// put NFC to sleep
			mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
			if (ticks > 2 * 60 * 60)
			//if (ticks > 10)
			{
				ticks = 0;
				sendtoESP("b", measurepower());
			}
		}
		else
		{
			bool status = mfrc522.PICC_ReadCardSerial();
			Serial.println("got something");
			mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
			if (status)
			{
				//Serial.println("ready...");
				// Now a card is selected. The UID and SAK is in mfrc522.uid.
				// Dump UID
				//Serial.print(F("Card UID:"));
				String id = "";
				for (byte i = 0; i < mfrc522.uid.size; i++) {
					id = id + String(mfrc522.uid.uidByte[i], HEX);
				}
				bool cmdresult = sendtoESP("t", id);
				Serial.println(cmdresult);
				Serial.println(id);
			}
		}
		Serial.flush();
		Serial1.flush();
		// Enter power down state for 1 s with ADC and BOD module disabled
		LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
		//LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);
	}

As you might see, I did not want to change the library and introduced two additional functions in the code: mfrc522_fast_Reset and mfrc522_fastDetect. The latter does not really make any difference and I'll probably switch back to mfrc522.PICC_IsNewCardPresent()

hope this helps someone

oops. Forgot to uncomment one line in the code above. Should read

mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
// mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x5);

Here is the latest fast detection technique and some observations
For fast card detection, I’m now happy with the below functions

void mfrc522_fast_Reset()
bool mfrc522_fastDetect3()

without going into details, fastDetect3 is trying to check the device status without requiring data transfer from the mfrc. It returns true if detection gives anything other than timeout error. It could result in false positives (however I don’t see any false positives with device I have) and has to be followed by a slower library function (e.g. PICC_IsNewCardPresent) for an accurate detection. The detection cycle is around 1 mSec. This is much better than I expected at the beginning of the project!

Two AA batteries voltage is 3033 mV and stays constant after 7.5 days of operation. Given ADC bit accounts for 8 mV, this means less than 1 mV/day voltage drop. The result is so good that I built a "final version" of the device to run on three AA batteries (without step up regulator and MOSFET that I mentioned in the previous comment) and convinced it will stay on for a year or two. The original device on two AA batteries is still on and I will let you know when it stops working (as I explained before, I expect ESP to stop emitting when the voltage is around 3 V which should take place in a month or so)

the code:

	void mfrc522_fast_Reset()
	{
		digitalWrite(RST_PIN, HIGH);
		mfrc522.PCD_Reset();
		mfrc522.PCD_WriteRegister(mfrc522.TModeReg, 0x80);			// TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
		mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x43);		// 10μs.
	//	mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x20);		// test

		mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);		// Reload timer with 0x064 = 30, ie 0.3ms before timeout.
		mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
		//mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

		mfrc522.PCD_WriteRegister(mfrc522.TxASKReg, 0x40);		// Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
		mfrc522.PCD_WriteRegister(mfrc522.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)

		mfrc522.PCD_AntennaOn();						// Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
	}

	bool mfrc522_fastDetect3()
	{
		byte validBits = 7;
		MFRC522::StatusCode status;
		byte command = MFRC522::PICC_CMD_REQA;
		byte waitIRq = 0x30;		// RxIRq and IdleIRq
		byte n;
		uint16_t i;

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

		//mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Idle);			// Stop any active command.
		mfrc522.PCD_WriteRegister(MFRC522::ComIrqReg, 0x7F);					// Clear all seven interrupt request bits
		mfrc522.PCD_SetRegisterBitMask(MFRC522::FIFOLevelReg, 0x80);			// FlushBuffer = 1, FIFO initialization
		mfrc522.PCD_WriteRegister(MFRC522::FIFODataReg, 1, &command);			// Write sendData to the FIFO
		mfrc522.PCD_WriteRegister(MFRC522::BitFramingReg, validBits);			// Bit adjustments
		mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Transceive);				// Execute the command
		mfrc522.PCD_SetRegisterBitMask(MFRC522::BitFramingReg, 0x80);			// StartSend=1, transmission of data starts

		i = 10;
		while (1) {
			n = mfrc522.PCD_ReadRegister(MFRC522::ComIrqReg);	// ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
			if (n & waitIRq) {					// One of the interrupts that signal success has been set.
				break;
			}
			if (n & 0x01) {						// Timer interrupt - nothing received in 25ms
				return false;
			}
			if (--i == 0) {						// The emergency break. If all other conditions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down.
				return false;
			}
		}

		return true;
	}

commented

@akellai

Great job. I tried your idea with soft power down and extracting only important lines from MFRC522::PCD_CommunicateWithPICC() and it works fine! My detection cycle takes now cca 3.2 ms. I understand that most of it takes SPI communication between Atmega and RC522.

Right now I'm having problem that I don't see any difference in setting various values of MFRC522_SPICLOCK on oscilloscope. In mfrc522.cpp there is always:
SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0));
In my case it has no effect on SPI speed.
So I was trying adding this:
SPI.setClockDivider(MFRC522_SPICLOCK);
In that case SPI is much faster on oscilloscope (I get detection cycle down to 1 ms like you) but it seems that arduino and rc522 don't understand well each other. I will have to investigate it further.

@jk987,
may be stupid question, but did you not forget the timer reload register?

mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);		// Reload timer with 0x01E = 30, ie 0.3ms before timeout.
mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

This will force timeout in mfrc to occur much faster. I also was playing with SPI and did not see any significant difference (you'd have to change the speed in multiple places in the library). My SPI speed is library default

commented

@akellai

I think this will not be the problem. I'm having TReloadReg 0x0190 and I'm swapping only TPrescalerReg values 0x05 (when testing card) and 0xA9 (original value when reading and writing).

There is a weird in mfrc522.h that MFRC522_SPICLOCK is defined as SPI_CLOCK_DIV4 which I beleive is some one byte const.
But SPISettings() used in mfrc522.cpp expects frequency in Hz which is uint32.
(?)
On oscilloscope I can see that my current SPI speed is around 100 kHz, while SPI_CLOCK_DIV4 should be around 1 MHz.

try also swapping TReloadReg. Switch it from 0x001E back to 0x0190 after detection

commented

@akellai Wow! Great work!
I would really appreciate if you could upload the whole code to Github or a zip as I would like to see how you manage the communication between Arduino Pro Mini and ESP-01.
Are you sending the ESP-01 also to sleep mode?

here you go https://github.com/akellai/rfid-music
notes:

  • the current card detection is even more aggressive than in the code above. Now detection is ~.8 msec, but not sure how stable it is
  • I had bad luck buying 'wrong ESP-01 modules' at the first place, so I originally used CH_PD of ESP-01 instead of reset. This required some 300 msec reset pulse. Had to do some soldering to fix https://www.letscontrolit.com/forum/viewtopic.php?t=2780#p15058 , bought some good modules later and now in the process of modifying the code (e.g. now need to call WiFi.forceSleepWake() for the connection to work reliably)
  • using fixed IP(dummy) and UDP broadcasting for ESP to minimize the connection time. This is required for both battery saving and fast reaction
  • the two AA batteries box is still up and running after 33 days.
    image
commented

@akellai
Thank you for the upload.
I just run your code in a test sketch, mfrc522_fastDetect3() seams not to work. It never triggers true.

my sketch:

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         4          // D2 - Rst
#define SS_PIN          15         // D8 - SDA

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance
MFRC522::StatusCode status;
MFRC522::MIFARE_Key key;

int ticks = 0;


///////////////////////////////////////////////////// INIT

void mfrc522_fast_Reset()
{
  digitalWrite(RST_PIN, HIGH);
  mfrc522.PCD_Reset();
  mfrc522.PCD_WriteRegister(mfrc522.TModeReg, 0x80);      // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x43);   // 10μs.
  //  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x20);   // test

  mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);   // Reload timer with 0x064 = 30, ie 0.3ms before timeout.
  mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
  //mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

  mfrc522.PCD_WriteRegister(mfrc522.TxASKReg, 0x40);    // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
  mfrc522.PCD_WriteRegister(mfrc522.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)

  mfrc522.PCD_AntennaOn();            // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
}



///////////////////////////////////////////////////// SETUP

void setup() {
  Serial.begin(74880);   // Initialize serial communications with the PC

  pinMode(RST_PIN, OUTPUT);
  pinMode(SS_PIN, OUTPUT);

  SPI.begin();      // Init SPI bus

  digitalWrite(SS_PIN, HIGH);
  digitalWrite(RST_PIN, HIGH);

  mfrc522_fast_Reset();
  if (mfrc522.PICC_IsNewCardPresent())
  {
    Serial.println("WHY? NEEDED?");
  }

  Serial.println("SETUP DONE!");
}

///////////////////////////////////////////////////// LOOP

void loop() {
  ticks++;

  if (ticks > 100) {
    //Serial.println("TICK...");
    ticks = 0;
  }
  
  if (!mfrc522_fastDetect3()) {
    // Put READER to SLEEP
    mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
  }
  else
  {
    Serial.println("GOT a Card!");
    delay(1000);
  }


}


///////////////////////////////////////////////////// DETECT

bool mfrc522_fastDetect()
{
  byte bufferATQA[2];
  byte bufferSize = sizeof(bufferATQA);
  MFRC522::StatusCode sta = mfrc522.PICC_RequestA(bufferATQA, &bufferSize);
  return (sta == MFRC522::STATUS_OK || sta == MFRC522::STATUS_COLLISION);
}

bool mfrc522_fastDetect2()
{
  byte bufferATQA[2];
  byte bufferSize = sizeof(bufferATQA);
  MFRC522::StatusCode sta = mfrc522.PICC_RequestA(bufferATQA, &bufferSize);
  return (sta != MFRC522::STATUS_TIMEOUT);
}

bool mfrc522_fastDetect3()
{
  byte validBits = 7;
  MFRC522::StatusCode status;
  byte command = MFRC522::PICC_CMD_REQA;
  byte waitIRq = 0x30;    // RxIRq and IdleIRq
  byte n;
  uint16_t i;

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

  //mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Idle);      // Stop any active command.
  mfrc522.PCD_WriteRegister(MFRC522::ComIrqReg, 0x7F);          // Clear all seven interrupt request bits
  mfrc522.PCD_SetRegisterBitMask(MFRC522::FIFOLevelReg, 0x80);      // FlushBuffer = 1, FIFO initialization
  mfrc522.PCD_WriteRegister(MFRC522::FIFODataReg, 1, &command);     // Write sendData to the FIFO
  mfrc522.PCD_WriteRegister(MFRC522::BitFramingReg, validBits);     // Bit adjustments
  mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Transceive);        // Execute the command
  mfrc522.PCD_SetRegisterBitMask(MFRC522::BitFramingReg, 0x80);     // StartSend=1, transmission of data starts

  i = 10;
  while (1) {
    n = mfrc522.PCD_ReadRegister(MFRC522::ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
    if (n & waitIRq) {          // One of the interrupts that signal success has been set.
      break;
    }
    if (n & 0x01) {           // Timer interrupt - nothing received in 25ms
      return false;
    }
    if (--i == 0) {           // The emergency break. If all other conditions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down.
      return false;
    }
  }

  return true;
}

[ If mfrc522_fastDetect3 is looping, my multimeter shows me a draw of around 4mAh from the Card reader (LED on card reader removed). If a card is present it's around 5mAh. ]

your sketch works fine with my board:

SETUP DONE!
GOT a Card!
GOT a Card!
GOT a Card!
GOT a Card!

I noticed you are using different pins for RST/SS - should be no problem if you connected the board correctly. However 15 does not sound right for Pro Mini that I'm using. Does a standard example work on your board?

commented

@akellai I'm using a NodeMCU (ESP12 E), thats why the 15 (D8) Pin. It couldn't be the Pin as my main script is working fine and mfrc522_fastDetect1 and mfrc522_fastDetect2 are working fine as well. A bit weird...
I have a Arduino pro micro and a Arduino nano 3.0 here, I will try them now and report back. (Need first do solder them.)

Interesting. You won't be able to achieve any reasonably low consumption with ESP b/c its wakeup cycle from deep sleep is ~300 mSec (at least I gave up this idea).
If you did not disassemble your sketch, can you please try to uncomment the

mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Idle);

(shouldn't change anything IMHO). And also increase the timeout

i = 100;

instead of 10

commented

@DimaVIII @akellai
fastDetect1 and fastDetect2 are almost the same.

My opinion is that akellai has his fastDetect3 optimized to the edge and it can easilly happen that with other tags or other physical position of the reader and other metal things it doesn't work.

Here is my piece of code I'm using for detection:

  byte bufferATQA[2];
  byte bufferSize = sizeof(bufferATQA);

  //return mfrc522.PICC_WakeupA(bufferATQA, &bufferSize);

  byte command = mfrc522.PICC_CMD_WUPA;
  byte waitIRq = 0x30;
  byte sendLen = 1;
  byte * sendData = &command;
  byte txLastBits = 7;
  byte rxAlign = 0;
  byte bitFraming = (rxAlign << 4) + txLastBits;
  
  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x08);
  mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_Idle);
  mfrc522.PCD_WriteRegister(mfrc522.ComIrqReg, 0x7F);
  mfrc522.PCD_WriteRegister(mfrc522.FIFOLevelReg, 0x80);
  mfrc522.PCD_WriteRegister(mfrc522.FIFODataReg, sendLen, sendData);
  //mfrc522.PCD_WriteRegister(mfrc522.BitFramingReg, bitFraming);
  mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_Transceive);
  //mfrc522.PCD_SetRegisterBitMask(mfrc522.BitFramingReg, 0x80);
  mfrc522.PCD_WriteRegister(mfrc522.BitFramingReg, 0x80 | bitFraming);
  
  timeout_data timeout_break;
  timeout_break.delay_us = 500;
  timeSet(&timeout_break);

  uint16_t i;
  
  for (i = 2000; i > 0; i--) {
    byte n = mfrc522.PCD_ReadRegister(mfrc522.ComIrqReg);
    if (n & waitIRq) break;
    if (n & 0x01) return mfrc522.STATUS_TIMEOUT;
    if (timeOut(&timeout_break)) return mfrc522.STATUS_TIMEOUT;
  }
  if (i == 0) return mfrc522.STATUS_TIMEOUT;

  byte errorRegValue = mfrc522.PCD_ReadRegister(mfrc522.ErrorReg);
  if (errorRegValue & 0x13) return mfrc522.STATUS_ERROR;
  if (errorRegValue & 0x08) return mfrc522.STATUS_COLLISION;
  
  return mfrc522.STATUS_OK;

I think that the akellai magic was in setting TPrescalerReg and TReloadRegH/TReloadRegL. TReloadRegH/L I have 0x0200. But because I had a problem with this I ended up using my timeout_data and timeSet() and timeOut() which is something using inside standard function micros() for measuring time. For me it was the best way for testing if the card detection doesn't take too long.

commented

@akellai
This worked fine, my card is now getting detected.
I even could set i = 20; but it seams to be not stable. I will try to play around with the values.
My Multimeter shows a 7.4mAh while looping what is still quit

@jk987
I'm trying to implement your code, and got a error with timeout_data:

testtest:108: error: 'timeout_data' was not declared in this scope
   timeout_data timeout_break;

How do you declare it?

commented

@DimaVIII
Like I said timeout_data, timeSet() and timeOut() is my wrapper around micros()
You can imagine there something like:

unsigned long stop = micros() + 500;

...

if (micros() < stop) return mfrc522.STATUS_TIMEOUT;

Thought I'd put it here: 2AA battery device started to miss transmissions (voltage is 2,984V - close to expected). Detection still works okay but ESP fails to connect in ~20% of cases.
If you look at the voltage over the time, you'd notice a 'hockey stick' in the beginning - half of the available voltage was gone in the first 3 days when the detection cycle was a little over 10 mSec and the rest - in ~40 days (detection time is ~1 msec)

image

too bad this does not work for @jk987 but I like his new approach with the timer. Guess it does not make sense for me to try - don't break what's working;)

the only idea I currently have is @jk987 could possibly have different cpu frequency and that affects the number of cycles required. I don't believe in 'metal' or different kinds of cards (though I agree - sounds not unlikely). Just in case, my board is Arduino Pro Mini, 3.3V, 8 Mhz

@DimaVIII using 80 Mhz board as far as I understand and looks like every cycle for him is faster. BTW: @DimaVIII, I don't understand what device you are using to measure the current, but oscilloscope would definitely help. Also try increasing sleep to 60 sec - this way you at least will measure your 'sleep' current. Without the led it should be pretty low (tens uA or less). Is this the case?

3AA device is still running as expected and hope it'll continue this way for long

If metal is a issue use ferrite sheet under the antenna, btw nice work guys

This is my code. I use atmega328. but i run chip about 3 hour i can't read card. everybody can help me.

void mfrc522_fast_Reset()
{
  digitalWrite(RST_PIN, HIGH);
  mfrc522.PCD_Reset();
  mfrc522.PCD_WriteRegister(mfrc522.TModeReg, 0x80);      // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x43);   // 10μs.
  //  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x20);   // test

  mfrc522.PCD_WriteRegister(mfrc522.TReloadRegH, 0x00);   // Reload timer with 0x064 = 30, ie 0.3ms before timeout.
  mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);
  //mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

  mfrc522.PCD_WriteRegister(mfrc522.TxASKReg, 0x40);    // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
  mfrc522.PCD_WriteRegister(mfrc522.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)

  mfrc522.PCD_AntennaOn();            // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
}

void setup()
{
  Serial.begin(9600);
  rtc.begin();
  // attachInterrupt(0, blink, RISING);
  install_RFID();// cài đặt nhận bộ rfid
  install_io();
  //rtc.adjust(DateTime(2016, 12, 9, 9, 7, 0));
  DateTime now = rtc.now();
  day_string = check_number(now.day());
  month_string = check_number(now.month());
  hour_string = check_number(now.hour());
  minute_string = check_number(now.minute());
  year_string = check_number(now.year());
  // interrupts();
  // critical, time-sensitive code here
  cli();                                  // tắt ngắt toàn cục

  /* Reset Timer/Counter1 */
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 = 0;

  /* Setup Timer/Counter1 */
  TCCR1B |= (1 << CS11) | (1 << CS10);    // prescale = 64
  TCNT1 = 40536;
  TIMSK1 = (1 << TOIE1);                  // Overflow interrupt enable
  sei();
  // noInterrupts();
  out_poweron();
  mfrc522_fast_Reset();
  millis_off = millis();
}
ISR (TIMER1_OVF_vect)
{
  if (ok) {
    state_led = !state_led;
    digitalWrite(out_status_complete, state_led);
    //    Serial.println("aaa");
  }
}
void loop()
{
  //  count ++;
  // digitalWrite(out_status_complete, HIGH);
  //  mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange);
  /// lấy thông tin id của thẻ đang nhận
  //checkdata("MAS");
//  mfrc522_fast_Reset();
  ////////////////////////

  if (!mfrc522_fastDetect3()) {
    //if (!mfrc522.PICC_IsNewCardPresent()) {
    // put NFC to sleep
    mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
    //    if (ticks > 24 * 60 * 60)
  }
  else
  {
    bool status = mfrc522.PICC_ReadCardSerial();
    //Serial.println("got something");
    mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
    if (status)
    {
      codemain();
    }
  }
  mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);
  digitalWrite(out_status_complete, HIGH);
  Serial.println("sleep MCU");
  // delay(100);// nen xoa khi chay that
  // Enter power down state for 1 s with ADC and BOD module disabled
  Serial.flush();
  LowPower.powerDown(SLEEP_500MS, ADC_OFF, BOD_OFF);
  ///// end code
  mfrc522.PICC_HaltA(); // Stop reading
  mfrc522.PCD_StopCrypto1();
}
// void check hous and foget card
boolean checkdayoder() {
  //readfrom card
  // String temp_dayend = block_tostring(6, get_block_dayoderroom_end );
  String day_card = temp_dayend.substring(0, 2);
  Serial.print(day_card);
  String month_card = temp_dayend.substring(2, 4);
  Serial.println(month_card);
  int temp_hour_int = hour_string.toInt();
  if (day_card == day_string && month_card == month_string && temp_hour_int == 12) {
    return true;
  }
  else {
    return false;
  }

}

void codemain() {
  if (check() == true) {
//        digitalWrite(out_status_complete, LOW);
    delay(50);
    /// lấy thông tin id của thẻ đang nhận
    //checkdata("MAS");
    for (int i = 0; i < 4; i++) {
      readidCard[i] = mfrc522.uid.uidByte[i];
      Serial.print(readidCard[i]);
    }
    temp_kind_card =  get_kind_card();// sẽ trả về kiểu String cho cho biến kind_card_staff bằng true nếu cần
    // String temp_room1 = read_block_card(4, get_block_room_card);
    // String temp = read_block_card(4, get_block_room_card);
    a_cus[16];
    readBlock(get_block_dataid, a_cus);
    temp_room = block_tostring(4, get_block_room_card );
    tempint_forget_card = read_block_card (1, get_block_forget_card ).toInt(); // kiểm tra loại thẻ có phải thẻ mới hay
    temp_dayend = block_tostring(6, get_block_dayoderroom_end );
    temp_room1 = temp_room;
    temp_inf = temp_room;
    temp_string_kind_card_eeprom = read_eeprom(e_section, 3); /// doc tu eeprom nhận biết loại thẻ và phân cùng làm việc của cửa
    if (temp_kind_card == "INF") {
      out_poweron();
      Serial.println("the thong tin");
      // out_poweron();
      get_status_inf();
      out_poweroff();
    }
    if (temp_kind_card == "STF") {
      out_poweron();
      Serial.println("the nhan vien");
      // out_poweron();
      get_status_staff(temp_kind_card);
      //    get_status_mas(temp_kind_card);
      out_poweroff();
    }
    if (temp_kind_card == "SET") {
      out_poweron();
      Serial.println("the cai dat");
      //   out_poweron();
      get_status_set();
      out_poweroff();
    }
    if (temp_kind_card == "CUS") {
      out_poweron();
      Serial.println("The khach");
      //  out_poweron();
      get_status_cus(temp_kind_card);
      //  get_status_mas(temp_kind_card);
      out_poweroff();
    }
    if (temp_kind_card == "MAS") {
      out_poweron();
      Serial.println("the khan cap");
      // out_poweron();
      get_status_mas(temp_kind_card);
      out_poweroff();
    }


  }


}

bool mfrc522_fastDetect3()
{
  byte validBits = 7;
  MFRC522::StatusCode status;
  byte command = MFRC522::PICC_CMD_REQA;
  byte waitIRq = 0x30;    // RxIRq and IdleIRq
  byte n;
  uint16_t i;

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

  //mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Idle);      // Stop any active command.
  mfrc522.PCD_WriteRegister(MFRC522::ComIrqReg, 0x7F);          // Clear all seven interrupt request bits
  mfrc522.PCD_SetRegisterBitMask(MFRC522::FIFOLevelReg, 0x80);      // FlushBuffer = 1, FIFO initialization
  mfrc522.PCD_WriteRegister(MFRC522::FIFODataReg, 1, &command);     // Write sendData to the FIFO
  mfrc522.PCD_WriteRegister(MFRC522::BitFramingReg, validBits);     // Bit adjustments
  mfrc522.PCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Transceive);        // Execute the command
  mfrc522.PCD_SetRegisterBitMask(MFRC522::BitFramingReg, 0x80);     // StartSend=1, transmission of data starts

  i = 10;
  while (1) {
    n = mfrc522.PCD_ReadRegister(MFRC522::ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq
    if (n & waitIRq) {          // One of the interrupts that signal success has been set.
      break;
    }
    if (n & 0x01) {           // Timer interrupt - nothing received in 25ms
      return false;
    }
    if (--i == 0) {           // The emergency break. If all other conditions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down.
      return false;
    }
  }

  return true;
}
boolean check () {
  if ( ! mfrc522.PICC_IsNewCardPresent())
    return false;

  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial())
    return false;

  return true;
}
/*
   Typical pin layout used:
   -----------------------------------------------------------------------------------------
               MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
               Reader/PCD   Uno           Mega      Nano v3    Leonardo/Micro   Pro Micro
   Signal      Pin          Pin           Pin       Pin        Pin              Pin
   -----------------------------------------------------------------------------------------
   RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
   SPI SS      SDA(SS)      10            53        D10        10               10
   SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
   SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
   SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15a
*/

@cuong0202
can you clarify your question? Is detection not working? Or it's working but stops after 3 hours?

hi and good day
Thanks every one for the precious knowledge shared here .
I'm to start a project with this Ic for controlling cabinet locks . since it is battery powered, I will need to use the solution mentioned above .
@akellai Ihave been using esp 8266 in arduino during my recent projects so Im very interested in using the esp as the main MCU . have you used esp as a processor first then you decided to use mega328 ? have you got good result from esp finally?
can any one please conclude the best way and best code from what is tested above ?

@itbetters,
using esp 8266 was my original idea (same reasons as yours). However I've learned that it takes too long for it to wake from the deep sleep. Here is the link that I found after myself coming up to the conclusion that wake from sleep is taking hundreds (300 in my case) of milliseconds thus you cannot wake up every second and expect significant power savings:
https://forum.makehackvoid.com/t/esp8266-deep-sleep-cycle-times-and-power-consumption-with-wifi-off/776

As far as the best code - I believe both timer based approach suggested by @jk987 and my approach are valid, however you'd need to empirically decide (based on your cpu) what timeout is good in case of @jk987 or what number of cycles in case of my approach. Try different counts starting from two and up to 100 and see what's working for you. I'd also appreciate if you share your results if you go this route

@akellai
thanks for the useful link
@KarlSix you said (Still running on 4xAA batteries 6 months )
I must use 4xAA batteries for this project too . but my first problem is that I must find a good voltage regulator which is high efficiency and low quiescent current.
could you please help me to find the solution?

As I mentioned, I'm running Arduino ProMini with ESP-01 on three AA batteries. I'm using builtin ProMini voltage regulator (Don't remember where I saw it, but the efficiency should be within 80-90% and consistent with my measurements, so good enough).
So far running it for a little over 3 months and expect to run for another 6-9 months. Unfortunately my voltmeter has just died and I cannot measure the battery voltage to make more accurate prediction. Will post the result as soon I replace the multi-meter.
Update:
fresh 3xAA batteries - 4.85 V
3xAA batteries after running 95 days - 4.35 V
the voltage drop is ~5.26 mV/day on 3 AA batteries (comparing to less than 2 mV a day on 2 AA batteries)

estimated remaining time 1800/5.26=340 days. May be too optimistic, but will see

@ivoras and others
Hi. Please review #334 . Thanks.

Hi All, I'm trying to build a low power car alarm with rfid auth, so power save is a must.
However, I was thinking on using softpowerdown WITH Rx interrupt generation towards the arduino. Did anyone try that? Power usage?
For the duino I'm using the LowPower library, I'm just in the start of the project, so any ideas welcome :)

If I understood you correctly, you assume MFRC522 in power down mode can detect a card and generate the interrupt. I believe this is not true. Interrupts can be used for communication but the card won’t be detected in the power down mode.

Yes, that was my idea; the mfrc522's datasheet is not clear about it :(
I did give it a couple if tries, but did not work.

Btw, how are your batteries?

still running on 3 AA batteries. Shows 4.27 V after 188 days. I don't constantly monitor the power - just measure on occasions. Here is the data:
Start date 4/4/2017 - 4.85V
7/6/2017 - 4.35 V
10/9/2017 - 4.27 V
strange but discharge seems to slow down in the last 3 months. Have more than one Volt to go and arithmetic gives more than 30 months remaining

Hi
I saw the V1pr question in my email and became interested in the subject again .I put aside using the esp module and made my own board using atmega 328 microcontroller . Programming with arduino , and using 4 AA batteries with a LDO , I finally reached 90uA when sleep mode .
@akellai
I am thinking about a student time attendance system for class rooms now! it is a raw idea but first i want to check whether it is possible or not . could you please kindly share your project so that I can test what you have made before?
regards

@itbetters
the code for both esp and pro mini is still here https://github.com/akellai/rfid-music

is that what you asking for?

Oh thanks
I thought may be there have been some new changes for your last project
what about the schematic of connections ?

@itbetters
will try to add the schematics/pictures on the weekends

the power down is just one line:

		// put NFC to sleep
		mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_NoCmdChange | 0x10);

the code should be implemented in the new version of the library per @Rotzbua:
"Add power control functions #334"

namely
power_down() - Enables soft power down mode.
power_up() - Disables soft power down mode.

You can safely replace this line with the power_down() call. I wouldn't bother using power_up() as it would need additional cycles, but I could be wrong (could be that properly waiting for the oscillator to come up would result in faster and more reliable overall card detection?)

correcting myself: looks like in the final code the functions are
void PCD_SoftPowerDown();
void PCD_SoftPowerUp();

@akellai
Thanks
we are looking forward your schematic
regards

@akellai
As I searched for Arduino pro mini regulators, I found these regulators on the 3.3v boards:
KB33, S20K, F34V, L0RA, L0RB
and the first one (KB33) is most popular .The quiescent current for it (mic5205) is about 150uA which doesn't seem ideal since I have found some other regulators with 70uA or less !
what is the regulator on your arduino board?

@itbetters, not sure but it reads "DE=A10". Could it be RT9193?

@akellai Hello, I've been reading this and I think I will use you code in the project I'm working on. I just want to mention one thing: battery energy is not linear to battery voltage.

That is, just because you battery dropped 10% of the initial voltage doesn't mean that the energy is 10% less than initial. In fact, most battery drop comparable amounts of voltage right at the start, that will explain the hockey stick figure you saw.

Heres a diagram from a simple Duracell Coppertop AA battery:
duracell

It's a bit tricky to interpret but this graph shows, in a way, the energy consumption of a battery. Energy = Power x Time and Power = Voltages x Amperage. Since the amperage is held constant for this curve, then you can imply that voltage is directly proportional to power AND voltage is directly related to energy for this graph. That means that the area under the curve equals the total energy capacity of the battery.
area under

As you can note, the energy capacity is largely dependent on the current consumption of the device. To estimate the energy left in the battery by reading the voltage (note: the voltage should be operating voltage, not open circuit voltage) you can draw a line. Say the voltage of a single cell is 1.2v.

eneregy

The area in red represents the energy already consumed by the load at that voltage and the area in green represents the energy left in the battery. Even though the voltage is 75% the nominal voltage, the area looks to be 20% of the total capacity.

If I use your example, you stated that you voltage is 4.27 or 1.42 per cell. Assuming you used a duracell coppertop my best guess is that you battery is somewhere between 70-80% of total capacity.

One last thing to note: depending on you cutoff voltage, the energy left within the battery could be useful. The built-in regulator, MIC5205, has a 17mV dropout which is next to nothing. However, that means that once the cell voltage approaches 1V or 3V in series, the energy left inside won't be used and is therefore wasted. Looking at the graph, I would say that that is about 5% leftover which is very good.

This means the the usable capacity left in your batteries is about 65-75%. Assuming power consumption stays the same, the total lifespan of you batteries should be ~620 days and should be running out by Christmas this year.

I'm sorry to intrude with this! I just hope this proves useful for future readers.

@akellai #269 (comment)

This was exactly what I was looking for. Thank you so much. I was looking into how to get PICC_WakeupA working, but I haven't dived that deep into code before. This function does exactly what I needed it to do. However, I am using multiple RFID readers, so I've expanded on your code a little bit to make it work with multiple readers. See below.

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN         13          // Configurable, see typical pin layout above
#define SS_1_PIN        2         // Configurable, take a unused pin, only HIGH/LOW required, must be diffrent to SS 2
#define SS_2_PIN        3          // Configurable, take a unused pin, only HIGH/LOW required, must be diffrent to SS 1
#define SS_3_PIN        4
#define SS_4_PIN        5
#define SS_5_PIN        6
#define SS_6_PIN        7

#define NR_OF_READERS   6

byte ssPins[] = {SS_1_PIN, SS_2_PIN, SS_3_PIN, SS_4_PIN, SS_5_PIN, SS_6_PIN};

MFRC522 mfrc522[NR_OF_READERS];   // Create MFRC522 instance.

void setup() {
  Serial.begin(9600); // Initialize serial communications with the PC
  SPI.begin();        // Init SPI bus
  delay(500);

  for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) {
    mfrc522[reader].PCD_Init(ssPins[reader], RST_PIN); // Init each MFRC522 card
    delay(100);
    Serial.print(F("Reader "));
    Serial.print(reader);
    Serial.print(F(": "));
    mfrc522[reader].PCD_DumpVersionToSerial();
  }
}

/**
   Main loop.
*/
void loop() {

  for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) {

    mfrc522_fast_Reset(reader); // Sends along the value of the current reader.

    if (mfrc522[reader].PICC_IsNewCardPresent() && mfrc522[reader].PICC_ReadCardSerial()) {
      Serial.print(F("Reader "));
      Serial.print(reader);
      dump_byte_array(mfrc522[reader].uid.uidByte, mfrc522[reader].uid.size);
      Serial.println();

      mfrc522[reader].PICC_HaltA();
      mfrc522[reader].PCD_StopCrypto1();
    }
  }
}

void dump_byte_array(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

// Function requires an integer assigned to specific reader, see for loop above.
void mfrc522_fast_Reset(int reader) {
  digitalWrite(RST_PIN, HIGH);
  mfrc522[reader].PCD_Reset();
  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TModeReg, 0x80);      // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TPrescalerReg, 0x43);   // 10μs.
  //  mfrc522.PCD_WriteRegister(mfrc522.TPrescalerReg, 0x20);   // test

  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TReloadRegH, 0x00);   // Reload timer with 0x064 = 30, ie 0.3ms before timeout.
  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TReloadRegL, 0x1E);
  //mfrc522.PCD_WriteRegister(mfrc522.TReloadRegL, 0x1E);

  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].TxASKReg, 0x40);    // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
  mfrc522[reader].PCD_WriteRegister(mfrc522[reader].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)

  mfrc522[reader].PCD_AntennaOn();            // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
}

Do note: this code is a bit redundant in the way it's currently programmed. I could simply remove mfrc522[reader].PICC_HaltA(); and NOT use the void mfrc522_fast_Reset(), but that's not the point of me testing this. I want full control over which PICC to halt, to stay ready or to reset.

Again, thank you @akellai for sharing your code!

Also thanks to @octavio2895 for his addition to this discussion. This is indeed good information for anyone interested in doing any project working on batteries.

OS version: W10___

Arduino IDE version: 1.8.12
MFRC522 Library version: v1.4.8
Arduino device: SAMD21 adafruitfeather M0
MFRC522 device: RFID-RC522

Tried to save power by switching off and on when required.
I used the code as given by akellai . And use the fasttrack().
It works as a stand alone sketch, however when I combine it with other software in which the I use different spi settings it does not work anymore.
I suspect I have to use
SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0));
and SPI.endTransactions.
Where do I need to use those lines?
Before and after each call to MRFC522.xxxx ?

Help is appreciated

I made an example how to use ESP32's deep sleep mode with MFRC522. I haven't done exact measurements yet, but a quick test shows you should be in the <100µA region with a 1s interval. That's good enough for my application.

/* This is an example that show how to use MFRC522 with ESP32's deep sleep mode efficiently.
 * In the deep sleep stub handler a quick check to see if a card is present is done (~1.5ms).
 * Only if there is a card the full wake up sequence is initiated.
 *
 * In this handler almost all hardware is still uninitialized. Therefore all functions and data
 * must be put into RTC RAM. That means you can't call any of the IDF or Arduino functions.
 * Only ROM functions and direct register accesses are available.
 * Therefore all hardware access functions had to be reimplemented. All functions intended to be
 * called in deep sleep stub got an "ds" prefix.
 *
 * ATTENTION: Don't call these functions from your normal application. Only core 0 can access
 * RTC fast RAM and normal arduino programms run on core 1.
 *
 * TODO: Hardware reset is unsupported at the moment.
 */

/******************* Configuration *****************/
#define DEBUG_PRINT_ENABLED 1
#define MFRC_CS 27
#define MFRC_SCK 12
#define MFRC_MOSI 4
#define MFRC_MISO 0
#define sleep_time_in_us 1000000ll

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>

#include "rom/rtc.h"
#include "soc/rtc_cntl_reg.h"

MFRC522 mfrc522 { MFRC_CS, UINT8_MAX };

#if DEBUG_PRINT_ENABLED
#include "soc/uart_reg.h"
static const char RTC_RODATA_ATTR debug_fmt_str[] = "---------> dbg: %d\n";
static const char RTC_RODATA_ATTR stub_fmt_str[] = "Deep sleep stub entered!\n";
static const char RTC_RODATA_ATTR wake_fmt_str[] = "Card detected. Waking up!\n";
static const char RTC_RODATA_ATTR sleep_fmt_str[] = "Sleeping again!\n";
#define DEBUG_PRINT ets_printf
#else
#define DEBUG_PRINT(...) do {} while (0)
#endif

/* Remember register offsets in RTC memory as esp32_gpioMux table is not available in deep sleep stub. */
uint32_t RTC_DATA_ATTR cs_reg = esp32_gpioMux[MFRC_CS].reg;
uint32_t RTC_DATA_ATTR sck_reg = esp32_gpioMux[MFRC_SCK].reg;
uint32_t RTC_DATA_ATTR mosi_reg = esp32_gpioMux[MFRC_MOSI].reg;

#define MFRC_REGISTER_READ_TIME 7 //us, used to calculate timeouts

static inline __attribute__((always_inline)) void dsGpioAsOutput(uint8_t pin, uint32_t reg_offset)
{
    GPIO.enable_w1ts = ((uint32_t) 1 << pin);
    ESP_REG(DR_REG_IO_MUX_BASE + reg_offset) = ((uint32_t) 2 << FUN_DRV_S) | FUN_IE | ((uint32_t) 2 << MCU_SEL_S);
}

static inline __attribute__((always_inline)) void dsDigitalWrite(uint8_t pin, bool value)
{
    if (value) {
        GPIO.out_w1ts = ((uint32_t) 1 << pin);
    } else {
        GPIO.out_w1tc = ((uint32_t) 1 << pin);
    }
}

static inline __attribute__((always_inline))    uint32_t dsDigitalRead(uint8_t pin)
{
    return (GPIO.in >> pin) & 0x1;
}

/* Hardware SPI is not initialized in deep sleep stub. */
uint8_t RTC_IRAM_ATTR dsSpiTransfer(uint8_t data)
{
    dsDigitalWrite(MFRC_SCK, 0);
    for (int i = 0; i < 8; i++) {
        dsDigitalWrite(MFRC_MOSI, data & 0x80);
        dsDigitalWrite(MFRC_SCK, 1);
        data <<= 1;
        data |= dsDigitalRead(MFRC_MISO);
        dsDigitalWrite(MFRC_SCK, 0);
    }
    return data;
}

uint8_t RTC_IRAM_ATTR dsPCD_ReadRegister(MFRC522::PCD_Register reg)
{
    dsDigitalWrite(MFRC_CS, 0);
    dsSpiTransfer(0x80 | reg);
    uint8_t result = dsSpiTransfer(0);
    dsDigitalWrite(MFRC_CS, 1);
    return result;
}

void RTC_IRAM_ATTR dsPCD_WriteRegister(MFRC522::PCD_Register reg, uint8_t value)
{
    dsDigitalWrite(MFRC_CS, 0);
    dsSpiTransfer(reg);
    dsSpiTransfer(value);
    dsDigitalWrite(MFRC_CS, 1);
}

bool RTC_IRAM_ATTR dsMFRC522_FastDetect()
{
    dsPCD_WriteRegister(MFRC522::CollReg, 0);
    dsPCD_WriteRegister(MFRC522::ComIrqReg, 0x7F);
    dsPCD_WriteRegister(MFRC522::FIFOLevelReg, 0x80);
    dsPCD_WriteRegister(MFRC522::FIFODataReg, MFRC522::PICC_CMD_REQA);
    dsPCD_WriteRegister(MFRC522::BitFramingReg, 7);
    dsPCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_Transceive);
    dsPCD_WriteRegister(MFRC522::BitFramingReg, 0x80 | 7);
    // 50ms timeout. Much longer than required, but should not ever be used at all => does not matter.
    for (uint32_t timeout = 0; timeout < 50000 / MFRC_REGISTER_READ_TIME; timeout++) {
        uint8_t irq_flags = dsPCD_ReadRegister(MFRC522::ComIrqReg);
        if (irq_flags & 0x30) {
            // RxIrq || IdleIrq
            return true;
        }
        if (irq_flags & 0x01) {
            // TimeoutIrq
            return false;
        }
    }
    printf("Error\r\n");
    return false;
}

void RTC_IRAM_ATTR dsMFRC522_FastReset()
{
    dsGpioAsOutput(MFRC_CS, cs_reg);
    dsGpioAsOutput(MFRC_SCK, sck_reg);
    dsGpioAsOutput(MFRC_MOSI, mosi_reg);
    // TODO: Set hardreset pin
    // Reset & Wakeup
    dsPCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_SoftReset);
    while ((dsPCD_ReadRegister(MFRC522::CommandReg) & (1 << 4))) {
        // ~ 200us wake up time from power down
    }

    // Init timer
    dsPCD_WriteRegister(MFRC522::TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
    dsPCD_WriteRegister(MFRC522::TPrescalerReg, 67); // 13.56MHz / (2 * 67 + 1) = ~100kHz => 10μs
    dsPCD_WriteRegister(MFRC522::TReloadRegH, 0); // Reload timer with 30, ie 0.3ms before timeout.
    dsPCD_WriteRegister(MFRC522::TReloadRegL, 30);

    dsPCD_WriteRegister(MFRC522::TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
    dsPCD_WriteRegister(MFRC522::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)
    dsPCD_WriteRegister(MFRC522::TxControlReg, 0x83); // Antenna on
}

void RTC_IRAM_ATTR dsMFRC522_SoftPowerDown()
{
    dsPCD_WriteRegister(MFRC522::CommandReg, MFRC522::PCD_NoCmdChange | 0x10);
}

void RTC_IRAM_ATTR esp_wake_deep_sleep(void)
{
    DEBUG_PRINT(stub_fmt_str);
    dsMFRC522_FastReset();
    if (dsMFRC522_FastDetect()) {
        // Card detected => Wake up system
        DEBUG_PRINT(wake_fmt_str);
        esp_default_wake_deep_sleep();
        return;
    } else {
        // No card => go to sleep again
        dsMFRC522_SoftPowerDown();

        DEBUG_PRINT(sleep_fmt_str);
#if DEBUG_PRINT_ENABLED
        //Wait till uart buffer is empty
        while (REG_GET_FIELD(UART_STATUS_REG(0), UART_ST_UTX_OUT)) {

        }
#endif
        // https://gist.github.com/igrr/54f7fbe0513ac14e1aea3fd7fbecfeab with addition of setting new wake up time

        // Add a fixed time offset to the current wake up time
        uint64_t current_ticks = READ_PERI_REG(RTC_CNTL_SLP_TIMER0_REG) | (static_cast<uint64_t>(READ_PERI_REG(RTC_CNTL_SLP_TIMER1_REG)) << 32);
        // same as rtc_time_us_to_slowclk(sleep_time_in_us, esp_clk_slowclk_cal_get())
        uint64_t sleep_time_in_ticks = (sleep_time_in_us << 19) / REG_READ(RTC_SLOW_CLK_CAL_REG);
        uint64_t new_ticks = current_ticks + sleep_time_in_ticks;

        WRITE_PERI_REG(RTC_CNTL_SLP_TIMER0_REG, new_ticks & UINT32_MAX);
        WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, new_ticks >> 32);

        // Set the pointer of the wake stub function.
        REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t )&esp_wake_deep_sleep);
        // Go to sleep.
        CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
        SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN);
        // A few CPU cycles may be necessary for the sleep to start...
        while (true) {
            ;
        }
        // never reaches here.
    }
}

void setup()
{
    Serial.begin(115200);
    printf("setup() started\n");
    if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) {
        printf("Found card\n");
        SPI.begin(MFRC_SCK, MFRC_MISO, MFRC_MOSI); //Enable hardware SPI
        // Demo only: Dump card data
        if (mfrc522.PICC_ReadCardSerial()) {
            mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
        }
    } else {
        printf("No card present\n");
    }

    printf("Entering deep sleep\n");
    esp_deep_sleep(sleep_time_in_us);
}

void loop()
{

}

I made an example how to use ESP32's deep sleep mode with MFRC522. I haven't done exact measurements yet, but a quick test shows you should be in the <100µA region with a 1s interval. That's good enough for my application.

So I want to give your code a go, but a bit insure on the wiring between the ESP32 D1 mini and the RC522. How can I see what GIOP pins you are using on the ESP32?

Pin configuration is right at the beginning:

#define MFRC_CS 27
#define MFRC_SCK 12
#define MFRC_MOSI 4
#define MFRC_MISO 0

You should be able to use any pins you want.

Pin configuration is right at the beginning:

#define MFRC_CS 27
#define MFRC_SCK 12
#define MFRC_MOSI 4
#define MFRC_MISO 0

You should be able to use any pins you want.

Thanks that helped.

A nother question. I seem to have some issue scanning some of my NFC/RFID tags when running this code.
If I dont go into sleep mode i have no issues scanning the same cards.
Is there something i can try an adjust to help with this issue?

Also I'm trying to implement ESP NOW to push out the ID tag to a server. Would it be appropriate to call all the ESP now initialization in the void setup() after the card is found?

Well I power down both mcu (328p) and the the rc522 exit low power when card is detected. Still running on 4xAA batteries 6 months so far its a electronic safe hack rfid unlock. Hardware race conditions can be tricky.

I interests build rfid door lock rc522+mcu (328p) you can share information.

Well, I was also reading it but I haven't noticed any useful difference between soft power-down and hard power-down. (?)

And going to hard power-down is quite easy, I just set the RST pin low. To wake it up I call MFRC522.PCD_Init() and it sets RST pin high. You can also find that in PCD_Init() there is 50 milliseconds delay for crystal startup. I modified it for myself to only 5 ms and it seems to work well in my case.

And generally, for battery operation, right now I'm testing the mode that ATmega328 asks MFRC522 if there is any PICC, if not then ATmega sets MFRC's RST pin low and goes to sleep for a second itself. After a second ATmega wakes up, sets RST pin high, asks MFRC and then it all go to sleep again... (Of course if there is a PICC present then it is more complicated.)

And to be woken for as short time as possible I also noticed that in library there is set 25 ms timeout in TReloadReg registers. You can find it in PCD_Init(). This timeout is used in PCD_CommunicateWithPICC(). I shortened it to 10 ms and it seems to work. But like the wakeup delay I don't know what the right minimum is.

Hi .
How can I do a hard power down without connecting to a pin reset?

commented

How can I do a hard power down without connecting to a pin reset?

According to this (chapter 8.6.1, page 33) I guess you can't: https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf

Seems quite logical because how would you wake it up again?

thank you very much for your reply.
interested in a few things:

  1. RC522 without a reset R2 resistor I can’t make it work.
    set spleep mode 1 s
  2. every time the processor wakes up, the consumption returns on every wakeup power 100 Mka at rotary excitation ptreblem current 1mA and so on up to 10 mA and the cycle repeats.
    I turned off timer0 timer 1 timer but no result.

Thanks so much to @herm for that ESP32 code. I had to make a couple of modifications to get it working for me, which may be of help to anyone else trying to use it:

  1. Remove these lines:
uint32_t RTC_DATA_ATTR cs_reg = esp32_gpioMux[MFRC_CS].reg;
uint32_t RTC_DATA_ATTR sck_reg = esp32_gpioMux[MFRC_SCK].reg;
uint32_t RTC_DATA_ATTR mosi_reg = esp32_gpioMux[MFRC_MOSI].reg;

(gpioMux is obsolete in the current version of ESP32-IDF)

  1. Replace these lines:
dsGpioAsOutput(MFRC_CS, cs_reg);
dsGpioAsOutput(MFRC_SCK, sck_reg);
dsGpioAsOutput(MFRC_MOSI, mosi_reg);

with

dsGpioAsOutput(MFRC_CS, GPIO_PIN_MUX_REG[MFRC_CS]);
dsGpioAsOutput(MFRC_SCK, GPIO_PIN_MUX_REG[MFRC_SCK]);
dsGpioAsOutput(MFRC_MOSI, GPIO_PIN_MUX_REG[MFRC_MOSI]);

(Unlike gpioMux, GPIO_PIN_MUX_REG is available from within the stub, so no need to make special RTC variables anymore.)

  1. After the call to dsMFRC522_FastReset(); add
ets_delay_us(1000);

This allows an extra millisecond for the card to get ready. Without this, like @hemandk I was finding that detection was very hit and miss, and when it did work the effective range was drastically reduced. After trying various things I had a hunch that everything was just happening a bit too quickly, and tried this extra delay, and it fixed it totally. 1000 was my first guess, and turned out to be right on the money: 500 and even 750 are not enough. But I only have one MFRC522 so I don't know if it will be the same with every reader, there might be some variation.

Using this method I'm measuring an average current of just a few mA, about an order of magnitude less than my best previous method!

I would take advance of this topic to ask your kind suggestion.
My goal is to make something working as long the card is near the reader, I have some dubts:

  1. Should the card be moved away (powered down) and then close it again (powered up) to receive from it the code? Or there is a way with this library to "polling" the card when I want? SUpposing the card is always touching reader.
  2. Is it safe for the card to be powered on 24/7? Or evantually, does it has a limited number of "wake ups"?
  3. I would consider to poll the card every 10 seconds, for example, considering the card keeps near the reader, does the hard reset keep low for 9 seconds and 1second not, allow the process correctly? (time power up compared the time the card is transmitting, etc. all wil be succesfull at first read attempt?)
    WHat you racomend me for this purpose? Thank you

I think consumption can be much lower if wakeup stub code works on the ULP coprocessor of the ESP32.
I am going to try to convert the software spi code to ASM so that it can be run while device is sleeping and only wakes up if a card is detected.
It is not much harder than reading i2c temperature sensors with the ULP but only downside is all connected pins must be RTC GPIO to be controlled via ULP.

Pro tip: Only turn on the antenna right before issuing the start request to save extra power:

...
  PCD_WriteRegister(PCD_Register::CommandReg, PCD_Command::PCD_Transceive);
  PCD_WriteRegister(PCD_Register::TxControlReg,   0x83);  // Antenna on
  PCD_WriteRegister(PCD_Register::BitFramingReg, 0x80 | 7);
...

@Rotzbua This seemed like the most appropriate place to ask: I am running a small project off a 9v battery and would like to improve its lifespan. Currently it only lasts a few hours because its running items. Is there a way to run the reader on a low power mode only so that it detects an rfid tag and sends an interrupt so I can know when to turn everything else back on?

And is there a way to have it do this with the arduino on low power mode?

@Rotzbua This seemed like the most appropriate place to ask: I am running a small project off a 9v battery and would like to improve its lifespan. Currently it only lasts a few hours because its running items. Is there a way to run the reader on a low power mode only so that it detects an rfid tag and sends an interrupt so I can know when to turn everything else back on?

No, sorry. The whole point of this thread is because the MFRC522 cannot do low power card detection and has no way of waking up the MCU when it does detect a card. Polling is the only answer. With an ESP32 you can do this from a deep sleep stub (see herm's code earlier in the thread, and my updates to it); I don't know if there's anything similar on Arduino, but Arduino are usually not a great choice for low power. ESPs are better, and STMs better still (but harder to use). There are other RFID readers that support LPCD properly and can wake the MCU when they detect a card.

Is it possible to run the code on a board (PN532) with I2C protocol?
or it can work on a modified card like here:
https://forum.arduino.cc/t/rc522-rfid-rc522-switching-spi-to-uart-interface-or-i2c-possible/425741