Skip to main content

Building a 2x2 RFID board with RC522 + 74HC4067 + Raspberry Pi

· 8 min read
Oleksandr Zaitsev
CS Researcher at UMR SENS, CIRAD

Goal: build a 2x2 board, with one RFID reader per cell, using a multiplexer already so the design can later scale to 4x5.

Components​

  • 4 × RC522 RFID modules
  • 1 × 74HC4067 multiplexer
  • 1 × Raspberry Pi 3B+
  • 4 × RFID tags
  • 4 × 10 kΩ resistors
  • Jumper wires
  • External 3.3 V power supply (recommended)

Cell Layout​

Use this numbering:

+--------+--------+
| Cell 0 | Cell 1 |
| RC522 | RC522 |
| #0 | #1 |
+--------+--------+
| Cell 2 | Cell 3 |
| RC522 | RC522 |
| #2 | #3 |
+--------+--------+

The corresponding multiplexer channels are:

Cell 0 -> MUX C0 -> RC522 #0 SDA/SS
Cell 1 -> MUX C1 -> RC522 #1 SDA/SS
Cell 2 -> MUX C2 -> RC522 #2 SDA/SS
Cell 3 -> MUX C3 -> RC522 #3 SDA/SS

Raspberry Pi GPIO Pins Used​

Use these Raspberry Pi pins:

PurposeRaspberry Pi GPIOPhysical pin
SPI SCLKGPIO11Pin 23
SPI MOSIGPIO10Pin 19
SPI MISOGPIO9Pin 21
SPI CE0GPIO8Pin 24
RC522 resetGPIO25Pin 22
MUX S0GPIO17Pin 11
MUX S1GPIO27Pin 13
MUX S2GPIO23Pin 16
MUX S3GPIO24Pin 18
MUX enableGPIO22Pin 15
3.3 V3.3 VPin 1 or 17
GroundGNDPin 6, 9, 14, 20, 25, 30, 34, or 39

Connect Power​

Connect Ground First​

All grounds must be connected together.

Connect:

FromTo
Raspberry Pi GNDBreadboard ground rail
RC522 #0 GNDBreadboard ground rail
RC522 #1 GNDBreadboard ground rail
RC522 #2 GNDBreadboard ground rail
RC522 #3 GNDBreadboard ground rail
74HC4067 GNDBreadboard ground rail

If you use an external 3.3 V supply, also connect:

FromTo
External power supply GNDBreadboard ground rail

Connect 3.3 V Power​

For a 4-reader prototype, you can test using the Raspberry Pi 3.3 V pin.

Connect:

FromTo
Raspberry Pi 3.3 VBreadboard 3.3 V rail
RC522 #0 VCC / 3.3VBreadboard 3.3 V rail
RC522 #1 VCC / 3.3VBreadboard 3.3 V rail
RC522 #2 VCC / 3.3VBreadboard 3.3 V rail
RC522 #3 VCC / 3.3VBreadboard 3.3 V rail
74HC4067 VCCBreadboard 3.3 V rail

For the future 4x5 board, use an external 3.3 V power supply instead of the Pi 3.3 V pin.

Do not connect RC522 VCC to 5 V.

Connect Shared SPI Wires​

All four RC522 modules share the same SPI bus.

Connect these wires:

Raspberry PiRC522 #0RC522 #1RC522 #2RC522 #3
GPIO11 / SCLKSCKSCKSCKSCK
GPIO10 / MOSIMOSIMOSIMOSIMOSI
GPIO9 / MISOMISOMISOMISOMISO

This means:

Pi GPIO11 goes to SCK on all 4 readers.
Pi GPIO10 goes to MOSI on all 4 readers.
Pi GPIO9 goes to MISO on all 4 readers.

Connect Reset​

Connect one reset wire shared by all RC522 modules:

Raspberry PiRC522 #0RC522 #1RC522 #2RC522 #3
GPIO25RSTRSTRSTRST

This lets the Pi reset all RFID readers together.

Connect the 74HC4067 Control Pins​

The 74HC4067 has address pins S0, S1, S2, and S3.

These pins tell the multiplexer which channel to connect.

Connect:

74HC4067Raspberry Pi
S0GPIO17
S1GPIO27
S2GPIO23
S3GPIO24
ENGPIO22

The EN pin is usually active-low:

EN = HIGH -> multiplexer disabled
EN = LOW -> multiplexer enabled

During normal scanning, the script will:

  1. Disable the multiplexer.
  2. Choose a channel using S0-S3.
  3. Enable the multiplexer.
  4. Read the corresponding RC522.
  5. Disable the multiplexer again.

Connect the Multiplexer Signal Pin​

Connect the common signal pin of the multiplexer to the Raspberry Pi SPI chip-select pin.

Depending on your 74HC4067 board, this pin may be called:

  • SIG
  • COM
  • Z

Connect:

74HC4067Raspberry Pi
SIG / COM / ZGPIO8 / CE0

This means that the Raspberry Pi chip-select signal goes into the multiplexer.

The multiplexer then sends that signal to one selected RC522 reader.

Connect Each RC522 SDA/SS Pin to the Multiplexer​

The RC522 module usually labels its chip-select pin as:

  • SDA
  • sometimes SS
  • sometimes SDA/SS

This is not I2C SDA in this setup. On RC522 SPI modules, this pin is used as SPI chip select.

Connect:

CellRC522 moduleRC522 pin74HC4067 channel
0RC522 #0SDA/SSC0
1RC522 #1SDA/SSC1
2RC522 #2SDA/SSC2
3RC522 #3SDA/SSC3

So:

RC522 #0 SDA/SS -> 74HC4067 C0
RC522 #1 SDA/SS -> 74HC4067 C1
RC522 #2 SDA/SS -> 74HC4067 C2
RC522 #3 SDA/SS -> 74HC4067 C3

Do not connect the RC522 SDA/SS pins directly to the Raspberry Pi.

Do not connect the four SDA/SS pins together.

Add Pull-Up Resistors on SDA/SS​

Each RC522 SDA/SS pin must be pulled up to 3.3 V with a 10 kΩ resistor.

Connect:

RC522 pinResistorPower
RC522 #0 SDA/SS10 kΩ3.3 V
RC522 #1 SDA/SS10 kΩ3.3 V
RC522 #2 SDA/SS10 kΩ3.3 V
RC522 #3 SDA/SS10 kΩ3.3 V

In practice:

RC522 SDA/SS pin -> one side of 10 kΩ resistor
other side of resistor -> 3.3 V rail

Why this is needed:

When a reader is not connected through the multiplexer, its SDA/SS pin would otherwise float. The pull-up keeps it HIGH, which means deselected.

Complete Connection Checklist​

Raspberry Pi to RC522 Readers​

Raspberry PiConnects to
GPIO11 / SCLKSCK on all RC522
GPIO10 / MOSIMOSI on all RC522
GPIO9 / MISOMISO on all RC522
GPIO25RST on all RC522
3.3 VVCC on all RC522
GNDGND on all RC522

Raspberry Pi to 74HC4067​

Raspberry Pi74HC4067
GPIO8 / CE0SIG / COM / Z
GPIO17S0
GPIO27S1
GPIO23S2
GPIO24S3
GPIO22EN
3.3 VVCC
GNDGND

74HC4067 to RC522 Readers​

74HC4067RC522
C0RC522 #0 SDA/SS
C1RC522 #1 SDA/SS
C2RC522 #2 SDA/SS
C3RC522 #3 SDA/SS

Enable SPI on Raspberry Pi​

Run:

sudo raspi-config

Then:

Interface Options -> SPI -> Enable

Reboot:

sudo reboot

After reboot, check that SPI exists:

ls /dev/spidev*

You should see something like:

/dev/spidev0.0
/dev/spidev0.1

Install Python Dependencies​

Install required packages:

sudo apt update
sudo apt install -y python3-pip python3-gpiozero python3-spidev python3-rpi.gpio
pip3 install mfrc522

If pip3 install mfrc522 fails because of system package restrictions, use a virtual environment:

python3 -m venv ~/rfid-env
source ~/rfid-env/bin/activate
pip install mfrc522 spidev RPi.GPIO

Python Code: Scan the 2x2 Board​

import time
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522

# 74HC4067 address pins
MUX_S0 = 17
MUX_S1 = 27
MUX_S2 = 23
MUX_S3 = 24

# 74HC4067 enable pin
# Usually active-low:
# HIGH = disabled
# LOW = enabled
MUX_EN = 22

# Shared RC522 reset pin
RC522_RST = 25

# Number of cells in the 2x2 board
CELL_COUNT = 4

CELL_NAMES = {
0: "Cell 0: top-left",
1: "Cell 1: top-right",
2: "Cell 2: bottom-left",
3: "Cell 3: bottom-right",
}


def setup_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(MUX_S0, GPIO.OUT)
GPIO.setup(MUX_S1, GPIO.OUT)
GPIO.setup(MUX_S2, GPIO.OUT)
GPIO.setup(MUX_S3, GPIO.OUT)
GPIO.setup(MUX_EN, GPIO.OUT)

GPIO.setup(RC522_RST, GPIO.OUT)

# Disable mux at startup.
GPIO.output(MUX_EN, GPIO.HIGH)

# Keep RC522 readers out of reset.
GPIO.output(RC522_RST, GPIO.HIGH)


def disable_mux():
GPIO.output(MUX_EN, GPIO.HIGH)


def enable_mux():
GPIO.output(MUX_EN, GPIO.LOW)


def select_mux_channel(channel):
# Channel number is binary encoded on S0-S3.
GPIO.output(MUX_S0, GPIO.HIGH if (channel & 0b0001) else GPIO.LOW)
GPIO.output(MUX_S1, GPIO.HIGH if (channel & 0b0010) else GPIO.LOW)
GPIO.output(MUX_S2, GPIO.HIGH if (channel & 0b0100) else GPIO.LOW)
GPIO.output(MUX_S3, GPIO.HIGH if (channel & 0b1000) else GPIO.LOW)


def select_cell(cell_index):
# Connect Raspberry Pi CE0 to the SDA/SS pin of one RC522 reader.
disable_mux()
select_mux_channel(cell_index)

# Small settling delay after switching channel.
time.sleep(0.002)

enable_mux()

# Small delay before using SPI.
time.sleep(0.002)


def main():
setup_gpio()

# SimpleMFRC522 uses /dev/spidev0.0, which corresponds to CE0.
reader = SimpleMFRC522()

last_seen = {}

print("Scanning 2x2 RFID board.")
print("Press Ctrl+C to stop.")
print()

try:
while True:
for cell in range(CELL_COUNT):
select_cell(cell)

try:
tag_id, text = reader.read_no_block()
except Exception as error:
tag_id = None
text = None
print(f"{CELL_NAMES[cell]} -> read error: {error}")

if tag_id:
value = str(tag_id)

if last_seen.get(cell) != value:
last_seen[cell] = value
print(f"{CELL_NAMES[cell]} -> tag {value}")
else:
if last_seen.get(cell) is not None:
last_seen[cell] = None
print(f"{CELL_NAMES[cell]} -> empty")

disable_mux()

# Avoid scanning too aggressively.
time.sleep(0.05)

except KeyboardInterrupt:
print()
print("Stopping.")

finally:
disable_mux()
GPIO.cleanup()


if __name__ == "__main__":
main()

Expected Output​

When a tag is placed on Cell 0:

Cell 0: top-left -> tag 123456789

When a tag is removed:

Cell 0: top-left -> empty

When several tags are placed:

Cell 0: top-left -> tag 123456789
Cell 1: top-right -> tag 987654321
Cell 2: bottom-left -> tag 555555555
Cell 3: bottom-right -> tag 222222222