miguelbalboa / rfid

Arduino RFID Library for MFRC522
The Unlicense
2.79k stars 1.45k forks source link

Disconnect and reconnect MFRC522 #519

Closed Karshilistic closed 4 years ago

Karshilistic commented 4 years ago

Step 1: Describe your environment

Step 2: Describe the problem

I am attempting to program a method to allow for the reinitialization of a disconnected and reconnected MFRC522. I am prompted to do this after finding that the reader sometimes stops working even if the rest of the program is working alright. It can be as simple as detecting if the reader has been disconnected and restarting to reinitialize, or if possible reinitialize the reader without a restart (favourable) I am using ESP-RFID as the base project. RFID relevant methods are in rfid.esp.

1. I don't know if this is at all possible; is it either a limitation of SPI or MFRC522 (hardware or library) and can't be done?

In my attempts, I ran into the issue where calling mfrc522.PCD_Init(rfidss, UINT8_MAX); again after having called it once in the beginning of the program causes an Exception (9) reset (Load or store to an unaligned address)(see EDIT 1). Using PCD_PerformSelfTest() has the same effect. Only mfrc522.PCD_ReadRegister(mfrc522.VersionReg) can be called reliably to detect if the reader is present, but the reader can disconnect between two consecutive calls of this function and, even if the reader is unresponsive to card reads, the function would return the correct version.

EDIT 1: this is actually misleading. Adding a delay(150) anywhere in the periodic check, which is what the PCD_Reset() function takes approximately, causes the same reset, which leads me to believe it's just the ESP8266 being finicky.

2. Any suggestions on how I can achieve this, given the trials and info provided? Should I add debugging outputs to the library methods to try and trace where exactly the program hangs? EDIT: already tried adding some debug serial prints and I've observed the following:

3. How do I check if the reader is responsive for the case where the reader disconnects and reconnects between checks and the reset is not caught by mfrc522.PCD_ReadRegister(mfrc522.VersionReg)?

Relevant Code:

The following are the functions that handle the reconnection. Obviously, as stated above, they don't achieve .. anything desirable. Purely a work in progress to give you a sense. They don't catch the biggest issue: the reader disconnecting and reconnecting between checks.

void ICACHE_FLASH_ATTR setupMFRC522Reader(int rfidss, int rfidgain)
{
    SPI.begin();                                    // MFRC522 Hardware uses SPI protocol
    bool readerTest = isMFRC522ReaderConnected();   // Check if Reader is connected
    #ifdef DEBUG
        Serial.print(F("[ INFO ] MFRC522 connection:"));
        Serial.println(readerTest ? "ok" : "ERROR!");
    #endif
    if(readerTest)                                  // If reader is connected
    {
        Serial.println("1"); // for debug
        readerConnected = true;                     // set connected flag (since this function is called before readerCheck() is called the first time)
        Serial.println("2"); // for debug
        Serial.println(rfidss);
        mfrc522.PCD_Init(rfidss, UINT8_MAX);        // Initialize MFRC522 Hardware
        Serial.println("3"); // for debug, we never got to this point on second initialize
        // Set RFID Hardware Antenna Gain
        // This may not work with some boards
        mfrc522.PCD_SetAntennaGain(rfidgain);
    #ifdef DEBUG
        Serial.printf("[ INFO ] RFID SS_PIN: %u and Gain Factor: %u", rfidss, rfidgain);
        Serial.println("");
    #endif
    #ifdef DEBUG
        ShowMFRC522ReaderDetails();                 // Show details of PCD - MFRC522 Card Reader details
    #endif
    }

    readerCheckTimer.attach(readerCheckInterval, readerCheck);
}

/*     Function to check if MFRC522 reader is still connected.
    This won't work, however, for disconnects and reconnects that happen between checks.
    TO-DO: find alternative to checking version to test connection
    Karshi 25 Jun 2020 
*/
bool ICACHE_FLASH_ATTR isMFRC522ReaderConnected()
{
    // Get the MFRC522 software version
    byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
    #ifdef DEBUG
        Serial.println(F("[ INFO ] Checking MFRC522 connection:"));
        Serial.print(F("[ INFO ] MFRC522 Version: 0x"));
        Serial.println(v, HEX);
    #endif
    // When 0x00 or 0xFF is returned, communication probably failed
    if ((v == 0x00) || (v == 0xFF))
    {
        #ifdef DEBUG
            Serial.println(F("[ WARN ] Communication failure, check if MFRC522 properly connected"));
        #endif
        return false;
    }
    else return true;
}

/* Function called by timer to handle checking MFRC522 reader. Called periodically using a Ticker - Karshi 25 June 2020 */
void ICACHE_FLASH_ATTR readerCheck()                            // Tried with and without ICACHE_FLASH_ATTR
{
    if (!isMFRC522ReaderConnected())                            // Check if the reader is connected, if not
    {
        readerConnected = false;
        #ifdef DEBUG
            Serial.println(F("[ INFO ] Checking MFRC522 reader: DISCONNECTED"));
            Serial.println(F("[ INFO ] Checking for reconnection"));
        #endif
        readerCheckTimer.attach(5, readerCheck);                // Wait for reconnection
        // TODO: restart after number of iterations so we're not stuck here
        return;
    }
    else if (isMFRC522ReaderConnected() && !readerConnected)    // else if reader connected but was unconnected previously
    {
        readerConnected = true;
        #ifdef DEBUG
            Serial.println(F("[ INFO ] Checking MFRC522 reader: RECONNECTED"));
            Serial.println(F("[ INFO ] Attempting to reinitialize MFRC522 reader"));
        #endif
        setupMFRC522Reader(rfidss, rfidgain);                   // try to reinitialize the reader
        if (!isMFRC522ReaderConnected())                        // If reinitialization fails,
        {

            writeEvent("WARN", "rfid", "Reader unresponsive. System reboot.", "");
            #ifdef DEBUG
                Serial.println(F("[ WARN ] Reinitializing MFRC522 reader: FAILED"));
                Serial.println(F("[ WARN ] Rebooting..."));
            #endif
            ESP.restart();                                      // Restart the ESP
        }
        else                                                    // Otherwise we're golden
        {
            #ifdef DEBUG
                Serial.println(F("[ INFO ] Reinitializing MFRC522 reader: SUCCESS"));
            #endif
            return;                                             // Exit the function
        }
    }
    // delay(150);    // Added to test Exception (9)

    // If connection is good, just let us know
    #ifdef DEBUG
        Serial.println(F("[ INFO ] Checking MFRC522 reader: CONNECTED"));
    #endif
}
Karshilistic commented 4 years ago

Commenting out the blocking (and honestly almost pointless) while loop in PCD_Reset() is allowing the code to continue without crashing, and the device re-initializes correctly. To check for reader responsiveness, I'm checking the modem status in Status2Reg instead of the version.

Rotzbua commented 4 years ago

The library tries to reset by..

  1. hardware pin
  2. software

If 1. fails it automatically fallbacks to 2.

So if you disconnect mfrc hardware code alwas runs into case 2.

The software reset MFRC522::PCD_Reset() is not perfect. It has no timeout, so the program stuck. As you mentioned: removing this part will solve your problem. 👍