Building a 2x2 RFID board with RC522 + 74HC4067 + Raspberry Pi
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:
| Purpose | Raspberry Pi GPIO | Physical pin |
|---|---|---|
| SPI SCLK | GPIO11 | Pin 23 |
| SPI MOSI | GPIO10 | Pin 19 |
| SPI MISO | GPIO9 | Pin 21 |
| SPI CE0 | GPIO8 | Pin 24 |
| RC522 reset | GPIO25 | Pin 22 |
| MUX S0 | GPIO17 | Pin 11 |
| MUX S1 | GPIO27 | Pin 13 |
| MUX S2 | GPIO23 | Pin 16 |
| MUX S3 | GPIO24 | Pin 18 |
| MUX enable | GPIO22 | Pin 15 |
| 3.3 V | 3.3 V | Pin 1 or 17 |
| Ground | GND | Pin 6, 9, 14, 20, 25, 30, 34, or 39 |
Connect Power​
Connect Ground First​
All grounds must be connected together.
Connect:
| From | To |
|---|---|
| Raspberry Pi GND | Breadboard ground rail |
| RC522 #0 GND | Breadboard ground rail |
| RC522 #1 GND | Breadboard ground rail |
| RC522 #2 GND | Breadboard ground rail |
| RC522 #3 GND | Breadboard ground rail |
| 74HC4067 GND | Breadboard ground rail |
If you use an external 3.3 V supply, also connect:
| From | To |
|---|---|
| External power supply GND | Breadboard ground rail |
Connect 3.3 V Power​
For a 4-reader prototype, you can test using the Raspberry Pi 3.3 V pin.
Connect:
| From | To |
|---|---|
| Raspberry Pi 3.3 V | Breadboard 3.3 V rail |
| RC522 #0 VCC / 3.3V | Breadboard 3.3 V rail |
| RC522 #1 VCC / 3.3V | Breadboard 3.3 V rail |
| RC522 #2 VCC / 3.3V | Breadboard 3.3 V rail |
| RC522 #3 VCC / 3.3V | Breadboard 3.3 V rail |
| 74HC4067 VCC | Breadboard 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 Pi | RC522 #0 | RC522 #1 | RC522 #2 | RC522 #3 |
|---|---|---|---|---|
| GPIO11 / SCLK | SCK | SCK | SCK | SCK |
| GPIO10 / MOSI | MOSI | MOSI | MOSI | MOSI |
| GPIO9 / MISO | MISO | MISO | MISO | MISO |
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 Pi | RC522 #0 | RC522 #1 | RC522 #2 | RC522 #3 |
|---|---|---|---|---|
| GPIO25 | RST | RST | RST | RST |
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:
| 74HC4067 | Raspberry Pi |
|---|---|
| S0 | GPIO17 |
| S1 | GPIO27 |
| S2 | GPIO23 |
| S3 | GPIO24 |
| EN | GPIO22 |
The EN pin is usually active-low:
EN = HIGH -> multiplexer disabled
EN = LOW -> multiplexer enabled
During normal scanning, the script will:
- Disable the multiplexer.
- Choose a channel using S0-S3.
- Enable the multiplexer.
- Read the corresponding RC522.
- 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:
SIGCOMZ
Connect:
| 74HC4067 | Raspberry Pi |
|---|---|
| SIG / COM / Z | GPIO8 / 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:
| Cell | RC522 module | RC522 pin | 74HC4067 channel |
|---|---|---|---|
| 0 | RC522 #0 | SDA/SS | C0 |
| 1 | RC522 #1 | SDA/SS | C1 |
| 2 | RC522 #2 | SDA/SS | C2 |
| 3 | RC522 #3 | SDA/SS | C3 |
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 pin | Resistor | Power |
|---|---|---|
| RC522 #0 SDA/SS | 10 kΩ | 3.3 V |
| RC522 #1 SDA/SS | 10 kΩ | 3.3 V |
| RC522 #2 SDA/SS | 10 kΩ | 3.3 V |
| RC522 #3 SDA/SS | 10 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 Pi | Connects to |
|---|---|
| GPIO11 / SCLK | SCK on all RC522 |
| GPIO10 / MOSI | MOSI on all RC522 |
| GPIO9 / MISO | MISO on all RC522 |
| GPIO25 | RST on all RC522 |
| 3.3 V | VCC on all RC522 |
| GND | GND on all RC522 |
Raspberry Pi to 74HC4067​
| Raspberry Pi | 74HC4067 |
|---|---|
| GPIO8 / CE0 | SIG / COM / Z |
| GPIO17 | S0 |
| GPIO27 | S1 |
| GPIO23 | S2 |
| GPIO24 | S3 |
| GPIO22 | EN |
| 3.3 V | VCC |
| GND | GND |
74HC4067 to RC522 Readers​
| 74HC4067 | RC522 |
|---|---|
| C0 | RC522 #0 SDA/SS |
| C1 | RC522 #1 SDA/SS |
| C2 | RC522 #2 SDA/SS |
| C3 | RC522 #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
