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();
}