Multiple Serial Ports with RFID readers

Hi all! I'm very new to arduinos and I'm working on a project that involves 4 RFID readers reading 4 different tags. It's for an escape room puzzle and essentially there are 3 distinct sequences of the 4 tags, and the players need to put the tags on the readers in the orders of the three different sequences, one after the other. For my practice setup, I just want each sequence to light up a different LED.

I was told (and I'm not sure now if this is accurate or not) that it's best practice to use a separate arduino for each RFID reader. So for my setup I have four Arduino Unos which each read a tag through a reader and output either '0', '1', '2' or '3' through the serial port to represent which tag they've read (or '4' if no tag is read).

These are then connected to the serial ports of an Arduino Mega, which determines whether the tags are in the next correct sequence and then lights up the corresponding LED if so. The setup is like this:

The code for the unos seems to be working fine, but I'm struggling to get the mega to turn the LEDs on correctly - when I connect all of the unos to the mega, only some of the serial inputs seem to be reading correctly and others don't. This is the code I've got currently for the arduino mega:

#define RED 53
#define GREEN 51
#define BLUE 49


void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial3.begin(9600);

  pinMode(RED, OUTPUT);
  pinMode(GREEN, OUTPUT);
  pinMode(BLUE, OUTPUT);
  digitalWrite(RED, LOW);
  digitalWrite(GREEN, LOW);
  digitalWrite(BLUE, LOW);
}


char sequences[3][4] = {
  {'0', '1', '2', '3'},   // 0th sequence
  {'1', '2', '3', '0'},   // 1st sequence
  {'2', '3', '0', '1'},   // 2nd sequence
};
int nextSequence = 0;
bool triggered = false;
char tags[4] = {'4', '4', '4', '4'};


bool arrayEq(char arr1[], char arr2[]) {
  for (int i=0; i<4; i++) {
    if (arr1[i] != arr2[i]) {
      return false;
    }
  }
  return true;
}


void loop() {
  // read all data from each serial buffer to ensure we don't build up a backlog and we're reading latest data
  while (Serial.available() > 0) {
    tags[0] = Serial.read();
  }
  while (Serial1.available() > 0) {
    tags[1] = Serial1.read();
  }
  while (Serial2.available() > 0) {
    tags[2] = Serial2.read();
  }
  while (Serial3.available() > 0) {
    tags[3] = Serial3.read();
  }

  // if any tags have been taken off their reader (and hence have sent data '4' to master chip), untrigger sequence
  if (triggered) {
    for (int i=0; i<4; i++) {
      if (tags[i] == '4')
        triggered = false;
        break;
      }
    }
  }

  // if not triggered, compare tags to latest sequence to see if we need to trigger
  else {
    // trigger sequence 0 (always allowed)
    if (arrayEq(sequences[0], tags)) {
      triggered = true;
      digitalWrite(RED, HIGH);
      delay(2000);
      digitalWrite(RED, LOW);
      if (nextSequence == 0) nextSequence = 1;
    }
    // trigger sequence 1 (allowed if sequence 0 has been triggered and sequence 3 hasn't yet)
    else if ((nextSequence == 1 || nextSequence == 2) && arrayEq(sequences[1], tags)) {
      triggered = true;
      digitalWrite(GREEN, HIGH);
      delay(2000);
      digitalWrite(GREEN, LOW);
      if (nextSequence == 1) nextSequence = 2;
    }
    // trigger sequence 2 (allowed only if sequence 0 and 1 have been triggered)
    else if (nextSequence == 2 && arrayEq(sequences[2], tags)) {
      triggered = true;
      digitalWrite(BLUE, HIGH);
      delay(2000);
      digitalWrite(BLUE, LOW);
      nextSequence = 0;
    }
  }

  delay(1000);
  Serial.print("inputs found: ");
  Serial.print(tags[0]);
  Serial.print(" ");
  Serial.print(tags[1]);
  Serial.print(" ");
  Serial.print(tags[2]);
  Serial.print(" ");
  Serial.print(tags[3]);
  Serial.println();
}

And this is the code that each Uno is running:

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN   9     // Configurable, see typical pin layout above
#define SS_PIN    10    // Configurable, see typical pin layout above

MFRC522 mfrc522(SS_PIN, RST_PIN);   // Create MFRC522 instance

/* Configure tag uids here */
byte uids[4][4] = {
  {0xD3, 0x19, 0x67, 0xB7}, // tag 1
  {0x82, 0x62, 0x7A, 0x09}, // tag 2
  {0x82, 0x05, 0x7E, 0x09}, // tag 3
  {0x7D, 0x47, 0x4F, 0x96}  // tag 4
};

void setup() {
  Serial.begin(9600);  // Initialize serial communications with the PC
  while (!Serial);     // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
  SPI.begin();         // Init SPI bus
  mfrc522.PCD_Init();  // Init MFRC522 card
}


int check_uid() {
  for (int i=0; i<4; i++) {
      bool match = true;
      for (int j=0; j<4; j++) {
        if (mfrc522.uid.uidByte[j] != uids[i][j]) {
          match = false;
          break;
        }
      }
      if (match) return i;
  }
  return 4;
}


void loop() {
  // Look for new cards, and send id '4' if none exists
  byte bufferATQA[2];
  byte bufferSize = sizeof(bufferATQA);
  MFRC522::StatusCode result = mfrc522.PICC_WakeupA(bufferATQA, &bufferSize);
  if ( result != MFRC522::STATUS_OK && result != MFRC522::STATUS_COLLISION && ! mfrc522.PICC_ReadCardSerial() )
  {
    Serial.write('4');
    Serial.flush();
    delay(500);
    return;
  }

  // Now a card is selected. Find its id and send it to the master chip
  int id = check_uid();
  mfrc522.PICC_HaltA();  // halt picc after using it, ready for next wake up call
  char buffer [1];
  char* strId = itoa(id, buffer, 10);
  Serial.write(strId);
  Serial.flush();
  delay(500);
}

My questions are:

  1. is there anything wrong with the serial connection setup I've shown in the diagram above, or should this in theory work to do what I want?
  2. assuming the wiring setup is fine, is anything about the code dodgy
  3. is this whole setup overkill? Would it be possible/advisable to run it all from just one uno or one mega? Or if not, is there a better way to communicate between the 5 chips?

Many thanks in advance for any insight into any of the above :slight_smile:

can you provide details of the RFID reader modules?
if they are SPI (which looks like from the diagram in post 1) you should be able to connect them all to a Mega SPI interface with each having a seperate SS (chip Select) pin

2 Likes

Yes it is a criminal waste of Arduinos, and I think shows your limited design experience.

Yes.

The whole design is going to blow up your RFID readers because these are 3V3 devices and you are going to hit them with Arduino 5V signals, which will ensure they have a very short life before they fail.

Why kill if you can't overkill? I'm more of an "why isn't what you are doing working" type.

Can you write and test a simple sketch for the mega that just echos the characters coming from the satellites?

Print the characters and which one they came from.

I haven't looked at the mega logic, in makes no snes to do until you are actually certainly receiving what you think you should be.

a7

1 Like

They're RFID-RC522s. Yeah I think I've seen an example online that uses one uno and multiple SS pins, I'd just heard that connecting many readers to one chip can be challenging and isn't necessarily advised. But it sounds like I was wrong on that and its better practice to use the one chip?

Yeah I've got that in the 'Serial.print' stuff the bottom of the mega code and I've tested it by just commenting out all of the LED stuff and just printing out the inputs. I found that it was working but only from some of the inputs some of the time, ie. occasionally RX1 and RX2 gave the right inputs and then occasionally it was RX2 and RX3 and the serial1 input just shows up as a box on the screen.
Did quite a lot of testing and couldn't find any pattern in which ones seemed to work and when. So I was wondering if maybe the way I'm using the Serial ports is incorrect or something

Multiple readers on one Uno, shouldn't be an issue - coder limited.
Multiple serial devices on one Mega, up to 3 shouldn't be an issue; again, coder limited.
Putting a 4th serial device on the Mega, best to use software serial for it (at 9600), because you should use Serial on the Mega for debugging using Serial Monitor. Get it all working, then, if you really must, reassign Serial to the fourth Uno.
But really, one Uno should handle 4 readers, if the coder is up to it. That, my friend, will be a test of skill.

1 Like

Understood, so you'd recommend 1 uno and just different SS pins then?

I have connected them at the 3V3 port though

Reader on Uno 3.3V output? That's a different issue; I don't use Unos, but I'd doubt the 3.3V supply onboard is good for the needs of a reader. Others will know.

1 Like

Sweet, thanks for the confirmation, that's the sense I've been getting. I'll give it a go with the many readers on one arduino then, may well post here again after for follow up questions!

With apologies in advance, your Mega code is seriously flawed. Any Mega code trying to juggle 4 serial devices MUST NOT have delay() code scattered through it, unless you dumb down the devices transmitting to it beyond any reasonable need. Sorry.

1 Like

As for an Uno doing the job, if you use delay() the way you did in the Mega, you'll not succeed. Time for a rethink, methinks.

Ah ok, thanks for the info. Why is that? I understand that having delay()s scattered about means that the mega won't necessarily be picking up data from the serial buffer as soon as it comes in so the buffer might build up a backlog of data; but in theory I thought the while loops that I have around the Serial.read()s at the top of my loop() function would solve this issue, since they ensure that all the data in each buffer is read and removed and only the most recent data is used.

Further observations:
If your diagram is complete, you're missing a ground wire from each Uno to the Mega. This wire is absolutely essential for reliable serial communication
<will add more to this reply as I dig into this, so watch for it>
In your Uno code,

  char buffer [1];
  char* strId = itoa(id, buffer, 10);

Why allocate a 1 char buffer length? That only has room for the null terminator, let alone whatever's being converted. Surely this will run amok?
<last last edit. No, it won't, as serial write only transfers one byte from that location>

As for the Mega, your Unos are only sending a single char each time, and waiting 500 ms between, so volume of data isn't big. No port overruns, unless you only extract one char each time through - with a delay(2000), a few will queue up, but you effectively flush that with your read while available stuff. But why then the delay(2000)?

Still, we can abandon this discussion if you're moving to a single device reading all 4. The limitation there is, they need to be within the bus length limitations distance wise, so you can't for example scatter the readers around a room. So we need to know much more about your project.

Final edit - yeah, I missed "escape room puzzle" - SPI won't likely cut it, nor will I2C, if you're going around a room. Possible, but not likely. They're really intended for on-board peripherals. So I suspect you're back to your Mega&4 Uno implementation, in which case my comments above are valid - you've got some tuning to do, and you may need to actually go to RS232 signal levels depending upon room size and topology, but it should be workable.

1 Like

That is only the power supply of the RFID chips. What causes the damage is the 5V signals from the Arduino, like the chip select, the clock and the MOSI pin. The 3V3 signal from the MISO is according to the data sheet is not enough either, although direct connection to the Arduino will probably work.

1 Like

the majority of the RFID-RC522 online tutorials show the SPI pins connected directly to the UNO SPI which could damage the device - very poor guidance!
if four RC522 devices can be connected to a single host a Arduino Due which uses 3.3V logic could be suitable
if individual hosts are required for the RC522 devices perhaps Arduino Pro Mini 3.3V would be a good choice - the RC522 certainly works with the Pro Mini 3.3V
e.g. running File>Examples>MFRC522v2>ReadUidMultiReader on a Pro Mini

Reader 1: Firmware Version: 0x92 = v2.0
Reader 2:  = (unknown)
WARNING: Communication failure, is the MFRC522 properly connected?
Reader 1: Card UID: C0 FF 62 1A
PICC type: MIFARE 1KB
Reader 2: Card UID: C0 FF 62 1A
PICC type: MIFARE 1KB
Reader 3: Card UID: C0 FF 62 1A
PICC type: MIFARE 1KB
Reader 4: Card UID: 15 64 FD C2
PICC type: MIFARE 1KB
1 Like

Yes I quite agree. Whenever I point this out in the forum the reply is always "but that is what they did on YouTube!".

There are many more 3V3 Arduinos that you can use as well as those you mentioned. However, the level shifting is easy to do with a potential divider on each output from the Arduino.

This is for a Raspberry Pi but applies to any situation where you want to cut down a 5V logic signal to a 3V3 one.

the critical question is how far apart are the RFID readers?
this will determine if four RFID readers can be attached to a single host micro

1 Like

Well it affects the sensitivity of each reader, that is the distance from the reader the card can be before reading it.

That is why on a door with an in and out reader the readers must be mounted on opposite sides of the door. If it were the same side then they would not work at all.

In this video I have three readers side by side. The cards have to be placed over the reader to reliably read it.

Many thanks for this. The ground wires missing seems to have been a major cause of the problem - I managed to get the multi-chip solution working once I connected ground wires. It's still pretty glitchy though, it often cuts if I move the wires slightly and then I can't get it to work again. I'm thinking that may mostly be a hardware issue though

Hmm yeah had forgotten I needed to leave space for the null terminator in the buffer. Weirdly this one didn't seem to cause any issues for me though, but have fixed it now anyway.

Yeah some of the delays have been left there accidentally from earlier testing. The main one I need is the delay(2000) after the LED lights up because I want it to turn on for a couple seconds then switch off again. I guess I could use a global counter or something instead though if needed.