RFID Reader and I2C Interference Help Needed

I have been working on a Christmas Challenge project for friends for Christmas. The basics are they each have an RFID tag and they need to place the tags on the readers in the correct order according to different clues for each round. Once all rounds solved the prize box opens. I have this working fine with 4 RFID readers. I need 8. I tried 8 on one Nano and it turned out to be too much copper and I had signal loss. No problem, I'll use two microcontrollers and have them each take half the readers and talk via I2C to relay when their "half" is solved. This works great for the first round but once the master I2C sends request to slave I2C to see if it's half is solved, it somehow affects the RFID string that is read by the readers so only the last two characters are read/stored. Obviously I need the whole string to verify if the tags are in the correct locations or not. Anyone have any ideas what's going on or have a better suggestion? hardware and code below.

I know there is a lot of bloat code below. I'm just using it to help debug. Once its working correctly, all that bloat code will go away.

Hardware:
"Master" Microcontroller - Adafruit Metro Mini 328 V2 - Arduino-Compatible - 5V 16MHz [STEMMA QT / Qwiic] : ID 5597 : $14.95 : Adafruit Industries, Unique & fun DIY electronics and kits
"Slave Microcontroller - ELEGOO Nano V3.0 Compatible With Arduino IDE (x3) – ELEGOO Official
RFID Reader - (new user so can only post 2 links) Sunfounder blue RFID kit. You can find it on google.

"Master" Code

/*
* "Object Placement" Puzzle
*
* This puzzle requires the players to place one or more items in the
* correct locations on sensor pads, at which point an RFID tag embedded in the object
* is detected by a sensor.
* When all sensors read the tag of the correct object, the puzzle round is solved.
*/


// LIBRARIES
// Standard SPI library comes bundled with the Arduino IDE
#include <SPI.h>
// RFID Reader library. Download and install from https://github.com/miguelbalboa/rfid
#include <MFRC522.h>
//LED control library
#include <FastLED.h> 
//Servo library, standard with Arudino IDE
#include <Servo.h>
// For playing sounds from DFPlayerMini module using the serial connection
// Download from https://github.com/DFRobot/DFRobotDFPlayerMini
#include "src/DFRobotDFPlayerMini/DFRobotDFPlayerMini.h"
// For serial connection to DFPlayer
#include <SoftwareSerial.h>
//Set up I2C Comm
#include <Wire.h>


// DEFINES
// Provides debugging information over serial connection if defined
#define DEBUG

//LED settings
#define NUM_LEDS 6
#define DATA_PIN 10
#define BRIGHTNESS 150 

//Define Rx, Tx for DFPlayer
#define RXD 17
#define TXD 16


// CONSTANTS
// The number of RFID readers
const byte numReaders = 4;
// Each reader has a unique Slave Select pin
const byte ssPins[] = {9,8,7,6};
// They'll share the same reset pin
const byte resetPin = 5;
// Specific NFC tag assigned to each player
const String player1 = "fdc3d2df";
const String player2 = "3dc4d2df";
const String player3 = "bdc3d2df";
const String player4 = "7dc3d2df";
const String player5 = "1dbdd2df";
const String player6 = "5dbdd2df";
const String player7 = "ddbcd2df";
const String player8 = "9dbcd2df";
// The sequence of NFC tag IDs required to solve the puzzle [rows] [columns]
const String correctIDs[10][4] = {player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 };

//These pins will be used to communicate to the servos that open the present box
const byte servoPresentPin = 14;
const byte servoLockPin = 15;
//create servos and name them
Servo myServoPresent;
Servo myServoLock;
//Idenitify I2C Slave Unit
const int slaveAddress = 1;


// GLOBALS
// Initialise an array of MFRC522 instances representing each reader
MFRC522 mfrc522[numReaders];
// The tag IDs currently detected by each reader
String currentIDs[numReaders];

//Denote which round of questions we are on
byte puzzleRound;
//Denote which song to play once round is won
byte solveMusic;
//Denote which clue to read
byte roundClue;
//Set a Flag to read the clue ot not
bool readClue;
//Flag to see if slave solved or not: 0 unsolved, 1 solved
byte flag;

//Define LED array
CRGB leds[NUM_LEDS];

// Create Software Serial connection for DFPlayer
SoftwareSerial softwareSerial(RXD, TXD);
//create object to access DFPlayer
DFRobotDFPlayerMini myDFPlayer;


/**
 * Initialisation
 */
void setup() {

  #ifdef DEBUG
  // Initialise serial communications channel with the PC
  Serial.begin(115200);
  Serial.println(F("Serial communication started"));
  #endif

    //Initialize the Puzzle Round and solve music
  puzzleRound = 0;
  solveMusic = 1;
  roundClue = 81;
  readClue = true;

  //Start I2C Comm as Master
  Wire.begin();

  //Set slave solve flag
  flag = 0;

  //FastLED LED array setup
  //Notation FastLED.addLeds<CHIPSET,DATA_PIN,COLOR_ORDER>(leds,NUM_LEDS);
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);

  //Set servos to servoPins and set initial position of servos
  myServoPresent.attach(servoPresentPin);
  myServoPresent.write(0);
  myServoLock.attach(servoLockPin);
  myServoLock.write(0);

  //Start DFPlayer
  Serial.print("Initialising serial interface to DFPlayer...");
  for(int i=0; i<10; i++) {
    softwareSerial.begin(9600);
    delay(500);
    if(myDFPlayer.begin(softwareSerial)){
      Serial.println(F("OK!"));
      break;
    }
    else { Serial.print("."); }
    if(i==9) {
      Serial.println(F("Failed :("));
      return;
    }
  }
  // Set volume (value from 0 to 30)
  myDFPlayer.volume(20);
  

	// We set each reader's select pin as HIGH (i.e. disabled), so 
	// that they don't cause interference on the SPI bus when
	// first initialised
	for (uint8_t i=0; i<numReaders; i++) {
		pinMode(ssPins[i], OUTPUT);
		digitalWrite(ssPins[i], HIGH);
	}
	
  // Initialise the SPI bus
  SPI.begin();

  for (uint8_t i=0; i<numReaders; i++) {
  
    // Initialise the readers
    mfrc522[i].PCD_Init(ssPins[i], resetPin);
    
	#ifdef DEBUG
    // Dump some debug information to the serial monitor
    Serial.print(F("Reader #"));
    Serial.print(i);
    Serial.print(F(" initialised on pin "));
    Serial.print(String(ssPins[i]));
    Serial.print(F(". Antenna strength: "));
    Serial.print(mfrc522[i].PCD_GetAntennaGain());
    Serial.print(F(". Version : "));
    mfrc522[i].PCD_DumpVersionToSerial();
	#endif
    
    // Slight delay before activating next reader
    delay(100);
  }
  

  #ifdef DEBUG
  Serial.println(F("--- END SETUP ---"));
  #endif
}



/**
 * Main loop
 */
void loop() {

  //Play Puzzle Challenge Clue, different per puzzleRound
  if (readClue == true) {
    myDFPlayer.playMp3Folder(roundClue);
    readClue = !readClue;
  }

  // Assume that the puzzle has been solved
  boolean puzzleSolved = true;

  // Assume that the tags have not changed since last reading
  boolean changedValue = false;

  // Loop through each reader
  for (uint8_t i=0; i<numReaders; i++) {

    // Initialise the sensor
    mfrc522[i].PCD_Init();
    
    // String to hold the ID detected by each sensor
    String readRFID = "";
    
    // If the sensor detects a tag and is able to read it
    if(mfrc522[i].PICC_IsNewCardPresent() && mfrc522[i].PICC_ReadCardSerial()) {
      // Extract the ID from the tag
      readRFID = dump_byte_array(mfrc522[i].uid.uidByte, mfrc522[i].uid.size);
    }
    
    // If the current reading is different from the last known reading
    if(readRFID != currentIDs[i]){
      // Set the flag to show that the puzzle state has changed
      changedValue = true;
      // Update the stored value for this sensor
      currentIDs[i] = readRFID;
    }
    
    // If the reading fails to match the correct ID for this sensor 
      if(currentIDs[i] != correctIDs[puzzleRound][i]) {
      // The puzzle has not been solved
      puzzleSolved = false;
    }

    // Halt PICC
    mfrc522[i].PICC_HaltA();
    // Stop encryption on PCD
    mfrc522[i].PCD_StopCrypto1(); 
  }

  #ifdef DEBUG
  // If the changedValue flag has been set, at least one sensor has changed
  if(changedValue){
    // Dump to serial the current state of all sensors
    for (uint8_t i=0; i<numReaders; i++) {
      Serial.print(F("Reader #"));
      Serial.print(String(i));
      Serial.print(F(" on Pin #"));
      Serial.print(String((ssPins[i])));
      Serial.print(F(" detected tag: "));
      Serial.println(currentIDs[i]);
    }
    Serial.println(F("---"));
  }
  #endif

  // If the puzzleSolved flag is set, all sensors detected the correct ID
  if(puzzleSolved){
    //debug code to see if Slave Communicarion is working
    Serial.print(F("Current Flag state: "));
    Serial.println(flag);
    Wire.requestFrom(slaveAddress, 1);
    flag = Wire.read(); // read one character from the I2C
    Serial.print(F("Slave Returned: "));
    Serial.println(flag);
    //Check to see if slave unit puzzle is also solved and if so Round is solved
    if(flag == 1){
    onSolve();
    }
  }
}

/**
 * Functions
 */

 //Called when correct puzzle solution has been entered
void onSolve(){
  #ifdef DEBUG
  // Print debugging message
  Serial.println(F("Puzzle Round Solved!"));
  #endif

  //Play a Christmas song Clip, different per puzzleRound
  myDFPlayer.playMp3Folder(solveMusic);

  // Advance the Puzzle Round and solve music, reset slave solved flag
  puzzleRound++;
  solveMusic++;
  roundClue++;
  readClue = !readClue;
  flag = 0;

  //Send new round Number to Slave
  //debug code to check slave communication
  Serial.println(F("Sending new puzzle round to Slave"));
  Wire.beginTransmission(slaveAddress); // transmit to slave device #1
  Wire.write(puzzleRound); // sends new PuzzleRound
  Wire.endTransmission(); // stop transmitting
  delay(200);
  Serial.println(F("Puzzle Round sent to Slave"));

  //Serial.print(F("Puzzle Round advanced to round "));
  Serial.print(F("Puzzle Round advanced to Round: "));
  Serial.println(puzzleRound);
  Serial.print(F("Solve Music advanced to Track: "));
  Serial.println(solveMusic);

  //Flash Lights Christmas Green then Red, i<45 times
  for (int i=0; i<45; i++) {
    for (int j=0; j<NUM_LEDS; j++) {
      leds[j] = CRGB::Green;
    }
    FastLED.show();     
    delay(250);
    for (int j=0; j<NUM_LEDS; j++) {
       leds[j] = CRGB::Red;
    }
    FastLED.show();    
    delay(250);
  } 

//Turn Lights White to show new round
  for (int i=0; i<NUM_LEDS; i++) {
      leds[i] = CRGB::White;
    }  
  FastLED.show(); 
  delay(5000);

// Check to see if completed final round else continue game
if (puzzleRound == 10){
  Serial.println(F("XMas Challenge has been Solved!"));

//Play End Game Congrat 
myDFPlayer.playMp3Folder(91);
delay(10000);
//Play worst Christmas Song Ever! Mariah Carey
myDFPlayer.playMp3Folder(92);
delay (53000);

//Servos to unlock and open present box
 myServoLock.write(90); 
 delay(500);
 myServoPresent.write(90); 

 //Play Victory Lights
for (int i=0; i<250; i++) {
    for (int j=0; j<NUM_LEDS; j++) {
      leds[j] = CRGB::Green;
    }
    FastLED.show();     
    delay(250);
    for (int j=0; j<NUM_LEDS; j++) {
       leds[j] = CRGB::Red;
    }
    FastLED.show();    
    delay(250);
    for (int j=0; j<NUM_LEDS; j++) {
       leds[j] = CRGB::White;
    }
    FastLED.show();    
    delay(250);
  } 

 }
//Turn Lights off
  for (int i=0; i<NUM_LEDS; i++) {
      leds[i] = CRGB::Black;
    }  
  FastLED.show();
 
}

/**
 * Helper function to return a string ID from byte array
 */
String dump_byte_array(byte *buffer, byte bufferSize) {
  String read_rfid = "";
  for (byte i=0; i<bufferSize; i++) {
    read_rfid = read_rfid + String(buffer[i], HEX);
  }
  return read_rfid;
}

"Slave" Code

* "Object Placement" Puzzle
*
* This puzzle requires the players to place one or more items in the
* correct locations on sensor pads, at which point an RFID tag embedded in the object
* is detected by a sensor.
* When all sensors read the tag of the correct object, the puzzle round is solved.
*/


// LIBRARIES
// Standard SPI library comes bundled with the Arduino IDE
#include <SPI.h>
// RFID Reader library. Download and install from https://github.com/miguelbalboa/rfid
#include <MFRC522.h>
//Wire Library for I2C Comm
#include <Wire.h>


// DEFINES
// Provides debugging information over serial connection if defined
#define DEBUG


// CONSTANTS
// The number of RFID readers
const byte numReaders = 4;
// Each reader has a unique Slave Select pin
const byte ssPins[] = {8,7,6,5};
// They'll share the same reset pin
const byte resetPin = 10;
// Specific NFC tag assigned to each player
const String player1 = "fdc3d2df";
const String player2 = "3dc4d2df";
const String player3 = "bdc3d2df";
const String player4 = "7dc3d2df";
const String player5 = "1dbdd2df";
const String player6 = "5dbdd2df";
const String player7 = "ddbcd2df";
const String player8 = "9dbcd2df";
// The sequence of NFC tag IDs required to solve the puzzle [rows] [columns]
const String correctIDs[10][4] = {player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 player1, player2, player3, player4,
                                 player5, player6, player7, player8,
                                 };


// GLOBALS
// Initialise an array of MFRC522 instances representing each reader
MFRC522 mfrc522[numReaders];
// The tag IDs currently detected by each reader
String currentIDs[numReaders];

//Denote which round of questions we are on
byte puzzleRound;

//Flag to tell Master if solved. 0 unsolved   1 solved
byte flag = 0;


/**
 * Initialisation
 */
void setup() {

  #ifdef DEBUG
  // Initialise serial communications channel with the PC
  Serial.begin(115200);
  Serial.println(F("Serial communication started"));
  #endif

  //Initialize the Puzzle Round
  puzzleRound = 0;
  

  //initiate I2C Slave
  Wire.begin(1);
  Wire.onReceive(receiveEvent); // register I2C event
  Wire.onRequest(requestEvent); // register I2C event

  // We set each reader's select pin as HIGH (i.e. disabled), so 
	// that they don't cause interference on the SPI bus when
	// first initialised
	for (uint8_t i=0; i<numReaders; i++) {
		pinMode(ssPins[i], OUTPUT);
		digitalWrite(ssPins[i], HIGH);
	}
	
  // Initialise the SPI bus
  SPI.begin();

  for (uint8_t i=0; i<numReaders; i++) {
  
    // Initialise the readers
    mfrc522[i].PCD_Init(ssPins[i], resetPin);
    
	#ifdef DEBUG
    // Dump some debug information to the serial monitor
    Serial.print(F("Reader #"));
    Serial.print(i);
    Serial.print(F(" initialised on pin "));
    Serial.print(String(ssPins[i]));
    Serial.print(F(". Antenna strength: "));
    Serial.print(mfrc522[i].PCD_GetAntennaGain());
    Serial.print(F(". Version : "));
    mfrc522[i].PCD_DumpVersionToSerial();
	#endif
    
    // Slight delay before activating next reader
    delay(100);
  }
  

  #ifdef DEBUG
  Serial.println(F("--- END SETUP ---"));
  #endif
}


/**
 * Main loop
 */
void loop() {

  // Assume that the puzzle has been solved
  boolean puzzleSolved = true;

  // Assume that the tags have not changed since last reading
  boolean changedValue = false;

  // Loop through each reader
  for (uint8_t i=0; i<numReaders; i++) {

    // Initialise the sensor
    mfrc522[i].PCD_Init();
    
    // String to hold the ID detected by each sensor
    String readRFID = "";
    
    // If the sensor detects a tag and is able to read it
    if(mfrc522[i].PICC_IsNewCardPresent() && mfrc522[i].PICC_ReadCardSerial()) {
      // Extract the ID from the tag
      readRFID = dump_byte_array(mfrc522[i].uid.uidByte, mfrc522[i].uid.size);
    }
    
    // If the current reading is different from the last known reading
    if(readRFID != currentIDs[i]){
      // Set the flag to show that the puzzle state has changed
      changedValue = true;
      // Update the stored value for this sensor
      currentIDs[i] = readRFID;
    }
    
    // If the reading fails to match the correct ID for this sensor 
      if(currentIDs[i] != correctIDs[puzzleRound][i]) {
      // The puzzle has not been solved
      puzzleSolved = false;
      flag = 0;
    }

    // Halt PICC
    mfrc522[i].PICC_HaltA();
    // Stop encryption on PCD
    mfrc522[i].PCD_StopCrypto1(); 
  }

  #ifdef DEBUG
  // If the changedValue flag has been set, at least one sensor has changed
  if(changedValue){
    // Dump to serial the current state of all sensors
    for (uint8_t i=0; i<numReaders; i++) {
      Serial.print(F("Reader #"));
      Serial.print(String(i));
      Serial.print(F(" on Pin #"));
      Serial.print(String((ssPins[i])));
      Serial.print(F(" detected tag: "));
      Serial.println(currentIDs[i]);
    }
    Serial.println(flag);
    Serial.println(F("---"));
  }
  #endif

  // If the puzzleSolved flag is set, all sensors detected the correct ID
  if(puzzleSolved){
    onSolve();
  }
}


/**
*Functions
*/

//tells slave unit to increase to next puzzle round if current round solved
void receiveEvent(int bytes) {
  puzzleRound = Wire.read(); // read one character from the I2C
  Serial.print("New puzzle round is Round: ");
  Serial.println(puzzleRound);
  flag = 0;
  Serial.print("Flag reset to:");
  Serial.println(flag);
}

//send current solved/unsolved state of slave unit to master to see if entire round is solved
void requestEvent() {
  Wire.write(flag); // send flag data to Master
  Serial.print("Flag sent to Master: ");
  Serial.println(flag);
}

//Called when correct puzzle solution has been entered
void onSolve(){

  #ifdef DEBUG
  // Print debugging message
  Serial.println(F("Puzzle Round Solved!"));
  #endif

  //Set Solve Flag
  flag = 1;
  Serial.println(flag);
  
}

/**
 * Helper function to return a string ID from byte array
 */
String dump_byte_array(byte *buffer, byte bufferSize) {
  String read_rfid = "";
  for (byte i=0; i<bufferSize; i++) {
    read_rfid = read_rfid + String(buffer[i], HEX);
  }
  return read_rfid;
}

type or paste code here

This is the serial monitor info from before the Master sends the I2C request and right after it sends it:

Reader #1 on Pin #8 detected tag: 7dc3d2df
Reader #2 on Pin #7 detected tag: fdc3d2df
Reader #3 on Pin #6 detected tag: ddbcd2df
---
Current Flag state: 0
Slave Returned: 0
Reader #0 on Pin #9 detected tag: df
Reader #1 on Pin #8 detected tag: df
Reader #2 on Pin #7 detected tag: df
Reader #3 on Pin #6 detected tag: df
---

Thank you for any and all help!!!!

The RFID reader: https://www.sunfounder.com/products/rfid-kit-blue

I'm afraid that I have bad news :cry: It is too much. You have rethink your project and maybe rebuild it.

Let's investigate the used libraries and buses and interrupts:

Master:

  1. FastLED for only 6 leds. It is only 6 leds, but the FastLED turns off interrupts to put out the pulse-train. The SoftwareSerial and the Wire library don't like that.
  2. RFID readers with SPI. That should work, but the wires for the SPI bus can not be very long.
  3. Servo. It generates the pulses in a interrupt. If you use a other libraries with interrupts or libraries that turn off interrupts, then you might hear a hickup, but that's not so bad.
  4. DF player with SoftwareSerial. The SoftwareSerial takes over the entire Arduino board. There is not much else that you can do.
  5. I2C bus. The I2C bus is not meant for communication between Arduino boards. The use of the Serial functions in the onReceive and onRequest handlers should only be done during testing.

Slave:

  1. Please don't use I2C address 1. Use something above 8, for example 0x20.
  2. Using the SPI bus for the RFID reader, together with the Wire library and the Serial output is all fine.
  3. The code flow is lacking transparency. A variable called 'flag' has little meaning, can you give it a better name ?

Did you know that projects fail when using the I2C bus between Arduino boards, especially when motors are involved. There about 10 golden rules to make it work, one of them is that SDA may not be next to SCL in a flat ribble cable.
The I2C bus is just one problem, you have a many more.

Do you have an Arduino Mega board ? If you can connect all RFID readers to a Mega board, then you don't have the SoftwareSerial and I2C bus anymore.

1 Like

Koepel, thank you so much for the excellent feedback and help! I am an amateur with this and just do stuff to tinker and have fun so I am lacking in a lot of the Golden Rules you referenced. I figured it had to be some sort of interrupt since everything works fine if I scale down the project to just 4 readers on one nano. Lights, music, round increments, it all works with just 4 RFID (actually I got 5 working reliably) and one nano. I do have a Mega but it won't fit the form factor I am trying to cram this all in. Plus I'm not sure if the Mega would be any different trying to run over 4 or 5 RFID's?
I have the next 4 days off so I'll be tinkering with this a lot. I'll definitely go through and make some changes and update you back. If I need to I can just scale back to 4 readers and have the players pair up into teams.
Thank you so much for taking the time to go through all this! I'll let you know how it goes!

Can you make a photo that shows the wires to the RFID readers ?
The Mega board has a microcontroller of the same "AVR" family as the Uno and the classic Nano board. It would not make a difference for the RFID readers.
Have you seen these, for extra pins via the I2C bus: https://www.adafruit.com/product/5346

try one Mega and omit Softserial.
For next Christmas you could try a Mega250 embedded PRO Mini - which comes in a smaller form factor - roughly the size of an Uno.

Also the Arduino Nano Every has several hardware Serials.

How long are all these SPI and I2C wires? Can you make a schematic with distance measurements and real pictures so we can get an idea how that looks like?

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.