rfid
rfid copied to clipboard
Power down modes?
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 :)
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.
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?
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);
}
}
@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
@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;
}
@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
@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
@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.
@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?
@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
@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.
@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?