I2C issue between multiple NANO + RC522

Hi,

For my project, I would to connect 7 RFID readers (RC522) to a Arduino UNO. I know it's not easy to have more than 4 of these readers on the same SPI bus and managed by the same Arduino. So I decided to have one Arduino Nano per RC522 (probably a bit overkill but I wanted to stay modular) and then connect them via I2C to the Arduino Uno (controller).

I created one PCB per « block », containing :

  • 1 RC522 RFID reader
  • 1 Arduino Nano (target)
  • DIP switches to set the I2C address
  • 1 LED
  • Terminal blocks to connect the PCBs

A4, A5, 5V and Ground of all the Arduino are connected together. A button connected to the controller trigger the request to all the targets to get the last RFID UID scanned.


/*
 * TARGET code (arduino Nano with RFID Reader)
 */

#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <arduino-timer.h>

#define SS_PIN 10
#define RST_PIN 9
#define PIN_LED 6
 
MFRC522 rfid(SS_PIN, RST_PIN);

MFRC522::MIFARE_Key key; 

// Init array that will store new NUID 
byte currentNFC[4];

bool detect = false;
byte no_count = 0;
byte no_max = 6;

//DIP switches for I2C Address
byte dip_pin[4] = {2,3,4,5};
int address = 8;

auto timer = timer_create_default();

void setup() { 
  Serial.begin(9600);
  SPI.begin(); // Init SPI bus
  rfid.PCD_Init(); // Init MFRC522

  pinMode(PIN_LED, OUTPUT); 

  //Address I2C
  for (byte i = 0; i < 4; i++) {
    pinMode(dip_pin[i], INPUT_PULLUP); 
    address += (!digitalRead(dip_pin[i])) * (1<<i);
  }

  Serial.println(address);

  Wire.begin(address);           // join i2c bus with address based on DIP
  Wire.onRequest(requestEvent); // register event

  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }

  Serial.println(F("This code scan the MIFARE Classsic NUID."));
  Serial.print(F("Using the following key:"));
  printHex(key.keyByte, MFRC522::MF_KEY_SIZE);

}
 
void loop() {

  if(detect){
    rfid.PCD_Init(); //reinit to detect removing
  }

  if ( ! rfid.PICC_IsNewCardPresent()){
    if(detect){
      no_count++;
      //Serial.print("?");

      if(no_count >= no_max){
        //NFC is removed 
        Serial.println("Removed");

        digitalWrite(PIN_LED, LOW);
        
        currentNFC[0] = 0;
        currentNFC[1] = 0;
        currentNFC[2] = 0;
        currentNFC[3] = 0;
        
        detect = false;
      }
    }
  }else{

    no_count = 0;

    // Verify if the NUID has been readed
    if ( ! rfid.PICC_ReadCardSerial())
      return;

    Serial.print(F("PICC type: "));
    MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak);
    Serial.println(rfid.PICC_GetTypeName(piccType));

    if (rfid.uid.uidByte[0] != currentNFC[0] || 
      rfid.uid.uidByte[1] != currentNFC[1] || 
      rfid.uid.uidByte[2] != currentNFC[2] || 
      rfid.uid.uidByte[3] != currentNFC[3] ) {
      Serial.println(F("A new card has been detected."));

      digitalWrite(PIN_LED, HIGH); // Allumage de la LED : état haut = HIGH
      detect = true;

      // Store NUID into currentNFC array
      for (byte i = 0; i < 4; i++) {
        currentNFC[i] = rfid.uid.uidByte[i];
      }
    
      Serial.println(F("The NUID tag is:"));
      Serial.print(F("In hex: "));
      printHex(rfid.uid.uidByte, rfid.uid.size);
      Serial.println();
      Serial.print(F("In dec: "));
      printDec(rfid.uid.uidByte, rfid.uid.size);
      Serial.println();
    }
    else Serial.println(F("Card read previously."));

    // Halt PICC
    rfid.PICC_HaltA();
  }

}


/**
 * Helper routine to dump a byte array as hex values to Serial. 
 */
void printHex(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

/**
 * Helper routine to dump a byte array as dec values to Serial.
 */
void printDec(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], DEC);
  }
}

void requestEvent() {
  if(detect){
    Serial.println(rfid.uid.uidByte[1]);
    Wire.write(currentNFC[0]);
    Wire.write(currentNFC[1]);
    Wire.write(currentNFC[2]);
  }else{
    Wire.write(0);
    Wire.write(0);
    Wire.write(0);
  }
}
/*
 * CONTROLER code (arduino Uno)
 */


#include <Wire.h>
#define DEVICES_NUM 7

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  pinMode(13, OUTPUT);
}

void loop() {
  for(byte i=0 ; i < DEVICES_NUM; i++){

    Wire.requestFrom(i+8, 3);    // request 6 bytes from peripheral device #8, 9, 10 etc.

    while (Wire.available()) { // peripheral may send less than requested
      byte c = Wire.read(); // receive a byte as character
      Serial.print(c);         // print the character
    }

    Serial.print(" | "); 
  
  }

  Serial.println(""); 

  digitalWrite(13, HIGH);
  delay(250);
  digitalWrite(13, LOW);
  delay(250);
}

My issue: everything works well up to 6 blocks. When I add the 7th, the I2C signal seems to get jammed and quickly freezes the Arduino Uno. I tried to switch hardware, reduce wire length, add pull-up resistance (probably not correctly) etc. But the 7th block always causes the issue.

So I know that I2C is not easy to use on these kind of wire lengths. I read a lot about this and I’m a bit lost, sorry if I make you repeat yourself. I don’t really understand how to choose (and connect) a pull-up resistance in my case. I guess I could also have other type of wires? Or maybe there is something else that could help me?

Thank you in advance for your help :slight_smile:

I suggest you rethink how you are building this. Also consider reliability, is it important or is intermittent operation OK. From your pictures you have already exceeded the comfortable wire length for I2C, it was designed for chip to chip communication on the same PCB (Printed Circuit Board).

If it were me I would use CAN, it is reliable and good for several thousand feet of wire. It is multi master but you can have one master and the rest slaves, that is up to your software. Each unit can also be a master and slave again your software determines this.

Thanks for your answer :slight_smile:

I will definitely look at the CAN protocol!

Sadly in the case of this project, I can’t change it now for timing reason (I should redo the PCBs among other things). Are there any other (smaller) changes I can do to improve the reliability?

some interesting comments seen in here that you might want to look at

I2C should only have one set of pull-ups
I see you have things daisy-chained so on the last board in the chain, put a 2.2K resistor pull-up on SCL and SDA

Thank you for your answers :slight_smile:
From the comments of the linked post, I’m afraid I’m still not able to really understand how to compute the value of the resistor :frowning:
I’ll try the 2.2k but I’m curious how you select this one?
So te be sure: I just have to connect the SDA and SCL connectors of the last board with resistors to the +5V?
I’ve not one but two “daisy-chained” branches, I should do that to the longest?
Thanks fo your help!

Do you know that SDA-pin of the RC552 RFID can be used as a Chip-select line? Using SDA-line you can operte all your 7 RFIDs using 4-Wire SPI Port one-after-another. You may consult the tutorial of this link.

That information is in the data sheet and the I2C specification. I have had good luck with 1mA but I do not exceed bus length.

Yes, I’m using this kind of connection with 1-3 RC522 :slight_smile: but, like many others on internet if im not mistaken, I can’t have more than 4 of them on the same Arduino without having issues, that’s why I’m trying something with one microcontroller per sensor.

In theory, a good number of SPI devices can be connected on the SPI Port/Bus. Try two and chcek that SDA-line works as a chip-select line. After that add the next one, and then... You will be able to find out the problem of not connecting more than 3.

I will definitively try it again the next time I need RC522 (it is really often ^^) but there is really something that seems hard to achieve with one Arduino and lot of these sensors, most threads I’ve seen on these subjects do not find a simple solution that only require to connect the sensors to one board, I’m afraid :frowning: And this is not my first attempt at trying to find a way :smiling_face_with_tear:

I know it is not the best way to look at your answers (thanks again!) but if there are things to try before changing my PCBs, as this project is time sensitive, I’m all ears ^^ I’ll try the pullup first (right now my issue is that I can’t reproduce the bug: everything works with 7 boards, but I know I’ve a reliability issue anyway…)

Hi there!

To give you an update:
When I wanted to test again, with an Arduino MEGA (which has pull-up resistors on SDA/SCL, maybe that was a factor) everything started working. I really tried a lot of configurations, I built the circuit in 4 copies, I tried to put various “interferences” but overall no more issue... including by switching back to a UNO :sweat_smile: So I'm a bit lost, I'm aware that the system must not be very robust with these cable lengths but for the moment it's difficult to debug.

I will clearly look at another way to do that in my next project, like CAN. But I have some doubts about the fact that I have to use a module like MCP2515, which uses SPI. And the RC522 also uses this interface, which means I'll have to share the bus between the two boards, I hope that won't cause any interference...

Also, ideally I'd like to find a solution that doesn't require 1 RC522, 1 Arduino and 1 MCP2515 per sensor. Does anyone have any ideas on how to keep it not too heavy but still retain the possibility of having such long cables?

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