Qwiic RFID keeps sending tag identifier, even if there is no tag present when quickly changing tags

I have built a test setup, using an arduino nano, a Qwiic Mux and 5 Qwiic RFID ID-XXLA’s (equipped with ID-3LA modules). Furthermore, there is a strip of 150 WS2812b led pixels that I have devided in code into 5 segments of 30 pixels.

I have 2 sets of 5 tags, each one associated with one of the readers (the “correct” tag).
If a reader detects one of the “correct” tags, the segment of pixels associated with the reader should light up green. If a reader detects one of the other tags, the segment should light up red.

After some tinkering, everything is working as I inteded, except for one thing.
When I try to “overload” the readers by presenting the tags very rapidly after another, at some point the readers start returning tag-ID’s, even if there is no tag present.
I think the problem is caused by the Qwiic RFID, as resetting the arduino or the Qwiic MUX doesn’t solve the problem, but pulling the reset pin on the Qwiic RFID low does.

I’ve tried adding a watchdog timer, but because the data that is returned is valid and the sketch doesn’t hang, that doesn’t work.

Could anyone shed some light on what could be the cause of this and maybe have an idea how to fix it?

Here’s my code:

#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <avr/wdt.h>  // Watchdog timer

#define DEBUG 1  // Set to 1 to enable debugging, 0 to disable

#define NUM_READERS 5
#define LEDS_PER_SEGMENT 30
#define TOTAL_LEDS (NUM_READERS * LEDS_PER_SEGMENT)
#define MUX_ADDRESS 0x70  // I2C address of the Qwiic Mux
#define RFID_ADDRESS 0x13 // I2C address of each RFID Qwiic Reader
#define BRIGHTNESS 250     // LED strip brightness (0-255)

#define LED_PIN 6  // Single LED strip connected to pin 6

Adafruit_NeoPixel strip(TOTAL_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

const byte correctTags[NUM_READERS][2][6] = {
    {{0x60, 0x00, 0xB2, 0x34, 0xE3, 0x05}, {0x60, 0x00, 0xB3, 0xAE, 0xA0, 0xDD}},
    {{0x60, 0x00, 0xB2, 0xD1, 0x26, 0x25}, {0x60, 0x00, 0xB3, 0x40, 0x61, 0xF2}},
    {{0x60, 0x00, 0xB3, 0x4A, 0xB6, 0x2F}, {0x60, 0x00, 0xB3, 0x7C, 0x45, 0xEA}},
    {{0x60, 0x00, 0xB3, 0x00, 0xE1, 0x32}, {0x60, 0x00, 0xB6, 0xDA, 0x8F, 0x83}},
    {{0x60, 0x00, 0xB1, 0xEE, 0xD6, 0xE9}, {0x60, 0x00, 0xB3, 0xB8, 0xC5, 0xAE}}
};

unsigned long ledTimers[NUM_READERS] = {0};
bool ledState[NUM_READERS] = {false};  // Track LED state (ON/OFF)
const unsigned long LED_DURATION = 5000; // LED hold time in milliseconds

void setup() {
    Serial.begin(9600);
    Wire.begin();

    strip.begin();
    strip.setBrightness(BRIGHTNESS);
    strip.show(); // Turn off all LEDs initially

    selectMuxPort(0xFF); // Disable all channels

    // Ensure all LEDs are OFF after reset
    turnOffAllLEDs();

    // Watchdog timer (reset after 2 seconds if stuck)
    wdt_enable(WDTO_2S);

    #if DEBUG
    Serial.println("Setup complete. Starting loop...");
    #endif
}

void loop() {
    wdt_reset();  // Reset watchdog timer

    unsigned long currentTime = millis();

    for (int i = 0; i < NUM_READERS; i++) {
        selectMuxPort(i);

        Wire.beginTransmission(RFID_ADDRESS);
        Wire.write(0x00); // Command to read tag
        Wire.endTransmission();

        delay(150);
        Wire.requestFrom(RFID_ADDRESS, 6);

        if (Wire.available() == 6) {
            byte tag[6];

            #if DEBUG
            Serial.print("Reader ");
            Serial.print(i);
            Serial.print(" detected tag: ");
            #endif

            for (int j = 0; j < 6; j++) {
                tag[j] = Wire.read();
                #if DEBUG
                Serial.print(tag[j], HEX);
                Serial.print(" ");
                #endif
            }

            #if DEBUG
            Serial.println();
            #endif

            if (isCorrectTag(i, tag)) {
                if (!ledState[i]) { // Only update if LED was OFF
                    lightSegment(i, 0, 255, 0); // Green for correct tag
                    ledTimers[i] = currentTime;
                    ledState[i] = true;
                    
                    #if DEBUG
                    Serial.println("Correct tag detected -> Lighting GREEN.");
                    #endif
                }
            } 
            else if (isInCorrectTagList(tag)) {
                if (!ledState[i]) { // Only update if LED was OFF
                    lightSegment(i, 255, 0, 0); // Red for incorrect tag
                    ledTimers[i] = currentTime;
                    ledState[i] = true;
                    
                    #if DEBUG
                    Serial.println("Incorrect but recognized tag -> Lighting RED.");
                    #endif
                }
            }
        }

        // Turn off LED after time elapsed
        if (ledState[i] && (currentTime - ledTimers[i] >= LED_DURATION)) {
            turnOffSegment(i);
            ledState[i] = false;
            
            #if DEBUG
            Serial.print("Segment ");
            Serial.print(i);
            Serial.println(" -> LED timeout -> Turning OFF.");
            #endif
        }

        selectMuxPort(0xFF);

        // Small delay to prevent rapid switching
        delay(100);
    }
}

// Turns off all LEDs at startup or after a reset
void turnOffAllLEDs() {
    #if DEBUG
    Serial.println("Turning off all LEDs (startup/reset).");
    #endif

    for (int i = 0; i < TOTAL_LEDS; i++) {
        strip.setPixelColor(i, 0);
    }
    strip.show();
}

void selectMuxPort(byte port) {
    Wire.beginTransmission(MUX_ADDRESS);
    Wire.write(1 << port);
    Wire.endTransmission();
}

bool isCorrectTag(int readerIndex, const byte *tag) {
    for (int k = 0; k < 2; k++) {
        bool match = true;
        for (int i = 0; i < 6; i++) {
            if (tag[i] != correctTags[readerIndex][k][i]) {
                match = false;
                break;
            }
        }
        if (match) return true;
    }
    return false;
}

bool isInCorrectTagList(const byte *tag) {
    for (int i = 0; i < NUM_READERS; i++) {
        for (int k = 0; k < 2; k++) {
            bool match = true;
            for (int j = 0; j < 6; j++) {
                if (tag[j] != correctTags[i][k][j]) {
                    match = false;
                    break;
                }
            }
            if (match) {
                return true;
            }
        }
    }
    return false;
}

void lightSegment(int segmentIndex, int r, int g, int b) {
    #if DEBUG
    Serial.print("Lighting segment ");
    Serial.print(segmentIndex);
    Serial.print(" -> RGB(");
    Serial.print(r);
    Serial.print(", ");
    Serial.print(g);
    Serial.print(", ");
    Serial.print(b);
    Serial.println(")");
    #endif

    int startLED = segmentIndex * LEDS_PER_SEGMENT;
    for (int i = startLED; i < startLED + LEDS_PER_SEGMENT; i++) {
        strip.setPixelColor(i, strip.Color(r, g, b));
    }
    strip.show();
}

void turnOffSegment(int segmentIndex) {
    #if DEBUG
    Serial.print("Turning off segment ");
    Serial.println(segmentIndex);
    #endif

    int startLED = segmentIndex * LEDS_PER_SEGMENT;
    for (int i = startLED; i < startLED + LEDS_PER_SEGMENT; i++) {
        strip.setPixelColor(i, 0);
    }
    strip.show();
}

Why are we trying to overload the reader?

Because it will be used as an interactive element in an exhibition and It has to be bulletproof. No matter how small the chance this will happen, once kids get in to the equasion, they’ll get it bricked in no time. And having someone monitor it constantly to cycle the power if this happens is not an option.

You can try a few things:

1. Implement a Debounce Mechanism

Add a debounce mechanism to filter out rapid, potentially erroneous readings:

#define DEBOUNCE_DELAY 50 // milliseconds

unsigned long lastReadTime[NUM_READERS] = {0};
byte lastTag[NUM_READERS][6] = {{0}};

// In the loop function, before reading a tag:
if (currentTime - lastReadTime[i] < DEBOUNCE_DELAY) {
    continue;
}

// After successfully reading a tag:
memcpy(lastTag[i], tag, 6);
lastReadTime[i] = currentTime;

2. Implement Tag Presence Check

Add a function to check if a tag is actually present:

bool isTagPresent() {
    Wire.beginTransmission(RFID_ADDRESS);
    Wire.write(0x01); // Command to check tag presence
    Wire.endTransmission();
    delay(10);
    Wire.requestFrom(RFID_ADDRESS, 1);
    return (Wire.available() && Wire.read() == 1);
}

// In the loop function, before reading a tag:
if (!isTagPresent()) {
    continue;
}

3. Reduce Delays and Optimize I2C Communication

Minimize delays and optimize I2C communication to improve responsiveness:

// Replace delay(150) with:
delayMicroseconds(50000);

// Use Wire.beginTransmission() only once per reader:
Wire.beginTransmission(RFID_ADDRESS);
Wire.write(0x00); // Command to read tag
Wire.endTransmission();
delayMicroseconds(50000);
Wire.requestFrom(RFID_ADDRESS, 6);

4. Implement Error Checking and Recovery

Add error checking and recovery mechanisms:

void resetRFIDReader(int readerIndex) {
    selectMuxPort(readerIndex);
    digitalWrite(RESET_PIN, LOW);
    delay(10);
    digitalWrite(RESET_PIN, HIGH);
    delay(50);
    selectMuxPort(0xFF);
}

// In the loop function, after reading a tag:
if (memcmp(tag, lastTag[i], 6) == 0 && currentTime - lastReadTime[i] < 1000) {
    errorCount[i]++;
    if (errorCount[i] > 5) {
        resetRFIDReader(i);
        errorCount[i] = 0;
    }
} else {
    errorCount[i] = 0;
}

5. Use Interrupts for Tag Detection

If your RFID readers support it, use interrupts for tag detection instead of polling:

const int interruptPins[NUM_READERS] = {2, 3, 4, 5, 7}; // Example pin numbers

void setup() {
    // ... other setup code ...
    for (int i = 0; i < NUM_READERS; i++) {
        pinMode(interruptPins[i], INPUT_PULLUP);
        attachInterrupt(digitalPinToInterrupt(interruptPins[i]), tagDetected, FALLING);
    }
}

volatile bool tagDetectedFlags[NUM_READERS] = {false};

void tagDetected() {
    for (int i = 0; i < NUM_READERS; i++) {
        if (digitalRead(interruptPins[i]) == LOW) {
            tagDetectedFlags[i] = true;
        }
    }
}

void loop() {
    for (int i = 0; i < NUM_READERS; i++) {
        if (tagDetectedFlags[i]) {
            // Read tag from reader i
            tagDetectedFlags[i] = false;
        }
    }
    // ... rest of the loop ...
}

Remember to test thoroughly and adjust the parameters (like debounce delay) as needed for your specific setup

I’ve changed the code, so the reader shouldn’t read tags when the leds are on.
I’ve also added a delay after the leds are turned off.

#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <avr/wdt.h>  // Watchdog timer

#define DEBUG 1  // Set to 1 to enable debugging, 0 to disable

#define NUM_READERS 5
#define LEDS_PER_SEGMENT 30
#define TOTAL_LEDS (NUM_READERS * LEDS_PER_SEGMENT)
#define MUX_ADDRESS 0x70   // I2C address of the Qwiic Mux
#define RFID_ADDRESS 0x13  // I2C address of each RFID Qwiic Reader
#define BRIGHTNESS 250     // LED strip brightness (0-255)

#define LED_PIN 6  // Single LED strip connected to pin 6

Adafruit_NeoPixel strip(TOTAL_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

const byte correctTags[NUM_READERS][2][6] = {
  { { 0x60, 0x00, 0xB2, 0x34, 0xE3, 0x05 }, { 0x60, 0x00, 0xB3, 0xAE, 0xA0, 0xDD } },
  { { 0x60, 0x00, 0xB2, 0xD1, 0x26, 0x25 }, { 0x60, 0x00, 0xB3, 0x40, 0x61, 0xF2 } },
  { { 0x60, 0x00, 0xB3, 0x4A, 0xB6, 0x2F }, { 0x60, 0x00, 0xB3, 0x7C, 0x45, 0xEA } },
  { { 0x60, 0x00, 0xB3, 0x00, 0xE1, 0x32 }, { 0x60, 0x00, 0xB6, 0xDA, 0x8F, 0x83 } },
  { { 0x60, 0x00, 0xB1, 0xEE, 0xD6, 0xE9 }, { 0x60, 0x00, 0xB3, 0xB8, 0xC5, 0xAE } }
};

unsigned long ledTimers[NUM_READERS] = { 0 };
bool ledState[NUM_READERS] = { false };   // Track LED state (ON/OFF)
const unsigned long LED_DURATION = 5000;  // LED hold time in milliseconds
const unsigned long READ_DELAY = 250;    // Delay before re-reading tag

void setup() {
  Serial.begin(9600);
  Wire.begin();

  strip.begin();
  strip.setBrightness(BRIGHTNESS);
  strip.show();  // Turn off all LEDs initially

  selectMuxPort(0xFF);  // Disable all channels
  turnOffAllLEDs();

  wdt_enable(WDTO_8S);

#if DEBUG
  Serial.println("Setup complete. Starting loop...");
#endif
}

void loop() {
  wdt_reset();  // Reset watchdog timer
  unsigned long currentTime = millis();

  for (int i = 0; i < NUM_READERS; i++) {
    if (ledState[i]) {
      if (currentTime - ledTimers[i] >= LED_DURATION) {
        turnOffSegment(i);
        ledState[i] = false;

#if DEBUG
        Serial.print("Segment ");
        Serial.print(i);
        Serial.println(" -> LED timeout -> Turning OFF.");
#endif
      }
      continue; // Skip reading tag if LEDs are still ON
    }

    selectMuxPort(i);

    Wire.beginTransmission(RFID_ADDRESS);
    Wire.write(0x00);  // Command to read tag
    Wire.endTransmission();

    delay(150);
    Wire.requestFrom(RFID_ADDRESS, 6);

    if (Wire.available() == 6) {
      byte tag[6];

#if DEBUG
      Serial.print("Reader ");
      Serial.print(i);
      Serial.print(" detected tag: ");
#endif

      for (int j = 0; j < 6; j++) {
        tag[j] = Wire.read();

#if DEBUG
        Serial.print(tag[j], HEX);
        Serial.print(" ");
#endif
      }

#if DEBUG
      Serial.println();
#endif

      if (isCorrectTag(i, tag)) {
        lightSegment(i, 0, 255, 0);  // Green for correct tag
        ledTimers[i] = currentTime;
        ledState[i] = true;

#if DEBUG
        Serial.println("Correct tag detected -> Lighting GREEN.");
#endif
      } else if (isInCorrectTagList(tag)) {
        lightSegment(i, 255, 0, 0);  // Red for incorrect tag
        ledTimers[i] = currentTime;
        ledState[i] = true;

#if DEBUG
        Serial.println("Incorrect but recognized tag -> Lighting RED.");
#endif
      }
    }

    selectMuxPort(0xFF);
    delay(READ_DELAY); // Add delay before reading the tag again
  }
}


void turnOffAllLEDs() {
  for (int i = 0; i < TOTAL_LEDS; i++) {
    strip.setPixelColor(i, 0);
  }
  strip.show();
}

void selectMuxPort(byte port) {
  Wire.beginTransmission(MUX_ADDRESS);
  Wire.write(1 << port);
  Wire.endTransmission();
}

bool isCorrectTag(int readerIndex, const byte *tag) {
  for (int k = 0; k < 2; k++) {
    bool match = true;
    for (int i = 0; i < 6; i++) {
      if (tag[i] != correctTags[readerIndex][k][i]) {
        match = false;
        break;
      }
    }
    if (match) return true;
  }
  return false;
}

bool isInCorrectTagList(const byte *tag) {
  for (int i = 0; i < NUM_READERS; i++) {
    for (int k = 0; k < 2; k++) {
      bool match = true;
      for (int j = 0; j < 6; j++) {
        if (tag[j] != correctTags[i][k][j]) {
          match = false;
          break;
        }
      }
      if (match) {
        return true;
      }
    }
  }
  return false;
}

void lightSegment(int segmentIndex, int r, int g, int b) {
  int startLED = segmentIndex * LEDS_PER_SEGMENT;
  for (int i = startLED; i < startLED + LEDS_PER_SEGMENT; i++) {
    strip.setPixelColor(i, strip.Color(r, g, b));
  }
  strip.show();
}

void turnOffSegment(int segmentIndex) {
  int startLED = segmentIndex * LEDS_PER_SEGMENT;
  for (int i = startLED; i < startLED + LEDS_PER_SEGMENT; i++) {
    strip.setPixelColor(i, 0);
  }
  strip.show();
}

Now, when I present a tag when the leds are on and, the Qwiic RFID still beeps to indicate that it has read a tag. When I wait until the leds are turned off, there is a small delay and the leds are turned on again.
What seems to be the case is, that the Qwiic RFID keeps reading tags, and storing them in memory. This data is sent to the arduino until the memory is empty.
So what I think I have to do is clear the Qwiic RFID’s memory after the leds have been turned off.
The thing is, I don’t know what command I need to send.
I’ve tried including the SparkFun_Qwiic_RFID_Arduino_Library and calling the clearTags function, but can’t get it to work.

I did some more testing.

When using this code:

#include <Wire.h>
#include "SparkFun_Qwiic_Rfid.h"

#define MUX_ADDR 0x70   // Default I2C address of TCA9548A
#define RFID_ADDR 0x13  // All RFID readers use the same I2C address

Qwiic_Rfid myRfid(RFID_ADDR);

// Function to select a channel on the I2C multiplexer
void selectMuxChannel(uint8_t channel) {
  if (channel > 7) return;  // TCA9548A supports channels 0-7
  Wire.beginTransmission(MUX_ADDR);
  Wire.write(1 << channel);  // Activate the selected channel
  Wire.endTransmission();
}

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("RFID Multi-Reader with I2C Multiplexer - Continuous Scanning");

  // Initialize all 5 RFID readers
  for (int i = 0; i < 5; i++) {
    selectMuxChannel(i);  // Switch to each reader
    delay(50);            // Allow time for switch
    if (!myRfid.begin()) {
      Serial.print("Error: RFID reader on channel ");
      Serial.print(i);
      Serial.println(" not found!");
    } else {
      Serial.print("RFID reader ");
      Serial.print(i);
      Serial.println(" initialized successfully.");
    }
  }

  Serial.println("Starting continuous scanning...");
}

void loop() {
  for (int i = 0; i < 5; i++) {
    selectMuxChannel(i);  // Switch to reader i
    delay(50);            // Short delay for I2C switch stabilization

    String tag = myRfid.getTag();
    if (tag.length() > 0) {  // Only print if a tag is detected
      Serial.print("Reader ");
      Serial.print(i);
      Serial.print(" - Tag ID: ");
      Serial.println(tag);
      delay(50);

      // Clear the tag from memory to prevent duplicate readings
      myRfid.clearTags();
      delay(50);      
    }
      Serial.print("Reader ");
      Serial.print(i);
      Serial.print(" - Tag ID: ");
      Serial.println(tag);
      delay(50);    
  }
  delay(250);  // Short delay before restarting the scan loop
}

The tags are read and their ID is printed twice. So that means the clearTags function doesn’t clear the memory?

I’ve rewritten the code and still have the same problem:

#include <Wire.h>
#include "SparkFun_Qwiic_Rfid.h"
#include <FastLED.h>

#define MUX_ADDR 0x70  // Default I2C address of TCA9548A
#define RFID_ADDR 0x13 // All RFID readers use the same I2C address
#define LED_PIN 6      // WS2812B Data pin
#define NUM_LEDS 150   // Total number of LEDs
#define SEGMENT_SIZE 30 // Each RFID reader gets 30 LEDs
#define DEBUG true // Set to false to disable debugging

Qwiic_Rfid myRfid(RFID_ADDR);
CRGB leds[NUM_LEDS];

struct LedSegment {
    int segment;
    CRGB color;
    unsigned long startTime;
    bool active;
};

LedSegment ledSegments[5];
const unsigned long LED_DURATION = 5000; // 5 seconds

const String correctTags[5][2] = {
    {"960178522275", "960179174160221"},  // Reader 0
    {"9601782093837", "9601796497242"},  // Reader 1
    {"9601797418247", "96017912469234"},  // Reader 2
    {"960179022550", "960182218143131"},  // Reader 3
    {"960177238214233", "960179184197174"}   // Reader 4
};

bool isValidTag(int readerIndex, String scannedTag) {
    return (scannedTag == correctTags[readerIndex][0] || scannedTag == correctTags[readerIndex][1]);
}

bool isTagFromOtherReader(int currentReader, String scannedTag) {
    for (int i = 0; i < 5; i++) {
        if (i != currentReader) {
            if (scannedTag == correctTags[i][0] || scannedTag == correctTags[i][1]) {
                return true;
            }
        }
    }
    return false;
}

void selectMuxChannel(uint8_t channel) {
    if (channel > 7) return;
    Wire.beginTransmission(MUX_ADDR);
    Wire.write(1 << channel);
    Wire.endTransmission();
}

void activateSegment(int segment, CRGB color) {
    ledSegments[segment].segment = segment;
    ledSegments[segment].color = color;
    ledSegments[segment].startTime = millis();
    ledSegments[segment].active = true;
}

void updateLeds() {
    unsigned long currentTime = millis();
    for (int i = 0; i < 5; i++) {
        if (ledSegments[i].active) {
            int startLED = ledSegments[i].segment * SEGMENT_SIZE;
            int endLED = startLED + SEGMENT_SIZE;
            if (currentTime - ledSegments[i].startTime < LED_DURATION) {
                for (int j = startLED; j < endLED; j++) {
                    leds[j] = ledSegments[i].color;
                }
            } else {
                for (int j = startLED; j < endLED; j++) {
                    leds[j] = CRGB::Black;
                }
                ledSegments[i].active = false;
            }
        }
    }
    FastLED.show();
}

void setup() {
    Wire.begin();
    Serial.begin(115200);
    if (DEBUG) Serial.println("RFID Multi-Reader with WS2812B LED Feedback");

    FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
    FastLED.clear();
    FastLED.show();

    for (int i = 0; i < 5; i++) {
        selectMuxChannel(i);
        delay(50);
        if (!myRfid.begin()) {
            if (DEBUG) {
                Serial.print("Error: RFID reader on channel ");
                Serial.print(i);
                Serial.println(" not found!");
            }
        } else {
            if (DEBUG) {
                Serial.print("RFID reader ");
                Serial.print(i);
                Serial.println(" initialized successfully.");
            }
        }
        ledSegments[i] = {i, CRGB::Black, 0, false};
    }

    if (DEBUG) Serial.println("Starting continuous scanning...");
}

void loop() {
    for (int i = 0; i < 5; i++) {
        selectMuxChannel(i);
        delay(50);

        String tag = myRfid.getTag();
        if (tag.length() > 0) {
            if (isValidTag(i, tag)) {
                if (DEBUG) {
                    Serial.print("Reader ");
                    Serial.print(i);
                    Serial.print(" - Tag ID: ");
                    Serial.print(tag);
                    Serial.println(" ✅ Valid Tag");
                }
                activateSegment(i, CRGB::Green);
            } else if (isTagFromOtherReader(i, tag)) {
                if (DEBUG) {
                    Serial.print("Reader ");
                    Serial.print(i);
                    Serial.print(" - Tag ID: ");
                    Serial.print(tag);
                    Serial.println(" ❌ Invalid Tag (Belongs to another reader)");
                }
                activateSegment(i, CRGB::Red);
            }
            
            // Clear all tags after processing
            myRfid.clearTags();
        }
    }

    updateLeds();
} 

Everything is working fine, until I start switching the tags around rapidly. At some point the Qwiic RFID starts sending tag ID’s, even if there is no tag detected.
It looks like there’s nothing I can implement in the arduino code, because the Qwiic RFID is working independently.
Trying to clear the memory either doesn’t work, or new data is just being generated all the time, so the buffer never gets cleared.
The only way to be sure the setup remains stable, is to reset the Qwiic reader every time the leds have been turned off. But that would mean having to deal with extra wiring and the readers being unresponsive whilst they reset.

You are likely just experiencing the limits of what a ~$20 RFID reader can do, methinks…might be time to take a look at the extremely capable/customizable M7E

I doubt it’s the RFID reader. That thing is doing it’s job. In my opinion the problem lies in the Qwiic board and it’s dodgy firmware. The whole concept that the thing keeps reading tags and pushing data without any way of controlling it. And then probably overflows and starts leading it’s own life. And it looks like I’m not the first one encoutering similar problems: https://community.sparkfun.com/t/sparkfun-rfid-qwiic-reader-returns-the-same-rfid-tag-value-over-and-over-after-about-12-scans/60169

To be honest, I went this route because I worked on another RFID project that used the ultra-cheap MFRC522 readers. I thought those were a hassle to get to work more or less reliably. I had a good experience in the past using the USB RFID reader in combination with ID_12LA’s, so I was hoping the Qwiic readers would be the same. Sadly, not so.

I think I’ll just have to reset the readers pre-emptively and hope things will work somewhat reliably…

BTW: 5 Qwiic RFID’s, 5 ID_3LA’s, a Qwiic Mux breakout and some Qwiic cables add up to well over €300,- where I live. So yeah, maybe I was expecting a bit more.

Its firmware is open-source and freely editable; the version it ships with is obviously not going to be able to take into account every conceivable implementation…especially if it is being intentionally overloaded :wink:

I still think the M7E is worth taking a look at for your use-case

Ok, I could try to do that. It seems that having the chip store just one tag instead of 20 might solve my problem.
Looking at the schematic, it seems that the SPI pins are broken out but not really accessible, unless you have or cobble up some sort of programming cable? (or solder jumper wires directly to the board). Something tells me that the board wasn’t designed with users uploading their own firmware in mind…

Well, this is interesting. After finding the post from august last year, where someone had the problem that, after reading 20 tags, the reader would just return the same tag value over and over, I decided to do a little test.
Instead of changing tags rapidly, I just offered one tag, waited until the leds were turned off (5 seconds, so no overloading), and lo and behold, after reading 20 tags, the leds just stayed on. So it’s not me overloading the reader, it’s the exact same problem the other user had!

After trying for an entire afternoon to change the firmware on the reader, I still haven’t managed to get a response from the board. I followed the instructions that are mentioned in the firmware (ATTinyCore/Installation.md at OldMaster---DO-NOT-SUBMIT-PRs-here-Use-2.0.0-dev · SpenceKonde/ATTinyCore · GitHub and ATTinyCore/ATtiny85_doc/README.md at OldMaster---DO-NOT-SUBMIT-PRs-here-Use-2.0.0-dev · SpenceKonde/ATTinyCore · GitHub)
I’ve soldered pins to GND, 3.3V, SDA (MOSI), SCL (SCK) and RST and a wire to the MISO test pad and connected them to GND, 3.3V, pin 11, pin 13, pin 10 and pin pin 12 on an arduino uno respectively (I’ve also tried using the ICSP pins). I uploaded the arduino isp sketch to the arduino uno and then put a 10uF capacitator between reset and ground. I uploaded the Attiny boards, selected the ATtiny84 and arduino as isp for the programmer. When I try to burn the bootloader, I get an error that says: “avrdude: Device signature = 0xffffff” which, according to what I found on the www could mean that there is a fault in the wiring.

To be honest, I wonder why there’s not more people bringing this up, as it seems the firmware just doesn’t work as intended. And changing the firmware may not be the biggest problem, getting it on to the board is not that easy…

Your assessment seems accurate, though these are all reasons why we offer different products at different price ranges…if it were trivial to overcome buffering issues we likely wouldn’t offer a far-better-performing product at all

Luckily, we do!