Fast detect on RC522 RFID and ESP32 - Card not detected

Hi,

I'm trying to make a small battery powered RFID Reader with and RC522 and ESP32 vemos D1 mini.

I found this thread github, where there is a suggestion for deep sleep card detection with RC522 and ESP32

I have a ESP Now server set up on a ESP8226 and that is working. The IDEA is to use ESP now to send the ID tag to save battery instead of powering up Wifi.

Using a standard tutorial online for an ESP32 and RC522 I can read all my tags (i only have 4 different ones).

If use the code from github I can only read 1 of the 4 tags.

My attemt to use the code from github and combine it with ESP now. It works for the 1 tag, but no luck on the rest.
I believe my issue is with the fast detection mode, but I can not figure out how to tweak it.

Any help would be much appreciated:

/* 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 *****************/
#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>

//ESP Now
#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>

#define DEBUG_PRINT_ENABLED 1
#define MFRC_CS 5
#define MFRC_SCK 18
#define MFRC_MOSI 23
#define MFRC_MISO 19
#define sleep_time_in_us 1000000ll

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

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 1
//MAC Address of the receiver 
uint8_t broadcastAddress[] = {0x5C, 0xCF, 0x7F, 0x9A, 0x09, 0x90};

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
  int id;
  char tag[14];
} struct_message;

//Create a struct_message called myData
struct_message myData;

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);
        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.
    }
}
// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

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
     
               
        if (mfrc522.PICC_ReadCardSerial()) {
          
            Serial.print("UID tag :");
            String content = "";
            byte letter;
            for (byte i = 0; i < mfrc522.uid.size; i++) 
            {
               Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
               Serial.print(mfrc522.uid.uidByte[i], HEX);
               content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
               content.concat(String(mfrc522.uid.uidByte[i], HEX));
            }
            Serial.println();
            //Write data out and to the myData struct
            //Set values to send
            myData.id = BOARD_ID;
            content.toCharArray(myData.tag, 14); 
            Serial.println(myData.tag);

            //Begin ESP NOW Setup

            // Set device as a Wi-Fi Station and set channel
            WiFi.mode(WIFI_STA);
            esp_wifi_set_promiscuous(true);
            esp_wifi_set_channel(9, WIFI_SECOND_CHAN_NONE);
            esp_wifi_set_promiscuous(false);
    
            //Init ESP-NOW
            int count1 = 0;
            while (esp_now_init() != ESP_OK) {
              delay(5);
              count1++;          
            }
            Serial.print("Number of 5ms to initialize: ");
            Serial.println(count1);
            // Once ESPNow is successfully Init, we will register for Send CB to
            // get the status of Trasnmitted packet
            esp_now_register_send_cb(OnDataSent);
            printf("ESP package sendt\n");
            //Register peer
            esp_now_peer_info_t peerInfo;
            memcpy(peerInfo.peer_addr, broadcastAddress, 6);
            peerInfo.encrypt = false;
            
            //Add peer
            int count2 = 0;        
            while (esp_now_add_peer(&peerInfo) != ESP_OK){
              delay(5);
              count2++; 
              //Serial.println("Failed to add peer");
            }
            Serial.print("Number of 5ms to add per: ");
            Serial.println(count2); 
            esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
            int count3 = 0;
            while (result != ESP_OK) {
              delay(5);
              count3++; 
            //Serial.println("Sent with success");
            }
            Serial.print("Number of 5ms to send and receive package: ");
            Serial.println(count3);
            
            //END OF ESP NOW
            /*
            else {
              Serial.println("Error sending the data");
            }*/
    
        }
        
    } else {
        printf("No card present\n");
    }
    
    printf("Entering deep sleep\n");
    esp_deep_sleep(sleep_time_in_us);
}

void loop()
{

}