はじめに
普段使っているICカード、ICタグモジュールをラズベリーパイpicoで操作してみます。
RFIDとは?
RFID(Radio Frequency Identification)は、電波を利用してタグの情報を非接触で読み取る技術です。一般的には、交通系ICカードや社員証などに使われています。
RFIDシステムは主に以下の3つの要素で構成されます
- タグ(ICチップとアンテナが埋め込まれたカードやシール)
- リーダー/ライター(タグの情報を読み取る装置)
- 制御システム(読み取ったデータを処理するコンピュータ)
RC522モジュールの作動原理
RC522は、13.56MHzの周波数帯を利用するRFIDリーダー/ライターです。タグと通信する際、電磁誘導を利用してタグに電力を供給し、データの送受信を行います。
- 13.56MHzの意味
RFIDにはいくつかの周波数帯がありますが、13.56MHzは高周波(HF: High Frequency)帯に属します。
この周波数は、
・近距離(数cm〜1m程度)の通信に適している
・電波の干渉が少なく、安定した通信が可能
・世界的に標準化されており、多くの交通系ICカード(Suica、PASMOなど)や電子マネーに採用されている
という特徴があります。 - 電磁誘導の仕組み
RFIDタグの中には小さなコイルがあり、RC522が発生させる電磁場の影響でコイル内に電流が流れます。この電流がタグのICチップに電力を供給し、情報をリーダーに送ることができます。 - タグとの通信
RC522は電磁界を発生させ、タグ内のコイルに電流を誘導し、エネルギーを供給します。 - データのやり取り
タグが保持する情報は、変調された信号としてRC522に送信され、SPI通信などを通じてマイコンに渡されます。 - 認証とデータ処理
一部のタグは暗号化機能を持ち、鍵を用いた認証プロセスを経てデータの読み書きを行います。
読み取ったUID(カードの識別番号)の意味
RFIDカードには、固有の識別番号(UID)が割り当てられています。このUIDは、RFIDリーダーによって読み取られ、一意の識別情報として使用されます。
- UIDの構造
一般的なUIDは4バイトまたは7バイトのデータで構成されており、各カードに固有の値が設定されています。 - 利用用途
UIDは、アクセス管理、電子マネー、勤怠管理システムなどで、個別のカードを識別するために利用されます。 - UIDは変更できる?
一般的な読み取り専用(Read-Only)RFIDカードのUIDは変更できません。ただし、書き換え可能なタイプのタグ(書き換え可能なMIFARE Classicなど)では、特定の条件下でUIDを書き換えることが可能な場合があります。
日常での活用例
RC522の動作原理は、交通系ICカード(例:SuicaやPASMO)と同じです。これらのICカードも13.56MHz帯のRFID技術を利用しており、改札機や電子マネー決済端末と通信することで、非接触でデータをやり取りします。
この技術は、電子錠や勤怠管理、在庫管理システムなど幅広い用途に活用されています。
配線
RC522はSPI通信を使用するため、PicoのSPI端子に接続します。
IRQ端子は今回は使用しません。
IRQ端子端子を使用するとメリットは多いので、別途チャレンジしてみてください。
・カードがかざされた時のみ処理を実行できる(ループ処理の削減)
・低消費電力(Picoをスリープにし、割り込みで復帰させることができる)
・リアルタイム性の向上
RaspberryPi Pico | RC522 |
VBUS(40番) | VCC |
GND(38番) | GND |
GP22(29番) | RST |
GP17(22番) | SDA |
GP18(24番) | SCK |
GP19(25番) | MOSI |
GP16(21番) | MISO |
MicropythonでRC522を動かす
動作にはライブラリが必要です。mfrc522.pyとして保存してください
from machine import Pin, SPI
from os import uname
class MFRC522:
OK = 0
NOTAGERR = 1
ERR = 2
REQIDL = 0x26
REQALL = 0x52
AUTHENT1A = 0x60
AUTHENT1B = 0x61
def __init__(self, spi, cs, rst):
self.spi = spi
self.cs = cs
self.rst = rst
self.rst.value(0)
self.cs.value(1)
self.spi.init()
self.rst.value(1)
self.init()
def _wreg(self, reg, val):
self.cs.value(0)
self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e)))
self.spi.write(b'%c' % int(0xff & val))
self.cs.value(1)
def _rreg(self, reg):
self.cs.value(0)
self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80)))
val = self.spi.read(1)
self.cs.value(1)
return val[0]
def _sflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) | mask)
def _cflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) & (~mask))
def _tocard(self, cmd, send):
recv = []
bits = irq_en = wait_irq = n = 0
stat = self.ERR
if cmd == 0x0E:
irq_en = 0x12
wait_irq = 0x10
elif cmd == 0x0C:
irq_en = 0x77
wait_irq = 0x30
self._wreg(0x02, irq_en | 0x80)
self._cflags(0x04, 0x80)
self._sflags(0x0A, 0x80)
self._wreg(0x01, 0x00)
for c in send:
self._wreg(0x09, c)
self._wreg(0x01, cmd)
if cmd == 0x0C:
self._sflags(0x0D, 0x80)
i = 2000
while True:
n = self._rreg(0x04)
i -= 1
if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
break
self._cflags(0x0D, 0x80)
if i:
if (self._rreg(0x06) & 0x1B) == 0x00:
stat = self.OK
if n & irq_en & 0x01:
stat = self.NOTAGERR
elif cmd == 0x0C:
n = self._rreg(0x0A)
lbits = self._rreg(0x0C) & 0x07
if lbits != 0:
bits = (n - 1) * 8 + lbits
else:
bits = n * 8
if n == 0:
n = 1
elif n > 16:
n = 16
for _ in range(n):
recv.append(self._rreg(0x09))
else:
stat = self.ERR
return stat, recv, bits
def _crc(self, data):
self._cflags(0x05, 0x04)
self._sflags(0x0A, 0x80)
for c in data:
self._wreg(0x09, c)
self._wreg(0x01, 0x03)
i = 0xFF
while True:
n = self._rreg(0x05)
i -= 1
if not ((i != 0) and not (n & 0x04)):
break
return [self._rreg(0x22), self._rreg(0x21)]
def init(self):
self.reset()
self._wreg(0x2A, 0x8D)
self._wreg(0x2B, 0x3E)
self._wreg(0x2D, 30)
self._wreg(0x2C, 0)
self._wreg(0x15, 0x40)
self._wreg(0x11, 0x3D)
self.antenna_on()
def reset(self):
self._wreg(0x01, 0x0F)
def antenna_on(self, on=True):
if on and ~(self._rreg(0x14) & 0x03):
self._sflags(0x14, 0x03)
else:
self._cflags(0x14, 0x03)
def request(self, mode):
self._wreg(0x0D, 0x07)
(stat, recv, bits) = self._tocard(0x0C, [mode])
if (stat != self.OK) | (bits != 0x10):
stat = self.ERR
return stat, bits
def anticoll(self):
ser_chk = 0
ser = [0x93, 0x20]
self._wreg(0x0D, 0x00)
(stat, recv, bits) = self._tocard(0x0C, ser)
if stat == self.OK:
if len(recv) == 5:
for i in range(4):
ser_chk = ser_chk ^ recv[i]
if ser_chk != recv[4]:
stat = self.ERR
else:
stat = self.ERR
return stat, recv
def select_tag(self, ser):
buf = [0x93, 0x70] + ser[:5]
buf += self._crc(buf)
(stat, recv, bits) = self._tocard(0x0C, buf)
return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR
def auth(self, mode, addr, sect, ser):
return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]
def stop_crypto1(self):
self._cflags(0x08, 0x08)
def read(self, addr):
data = [0x30, addr]
data += self._crc(data)
(stat, recv, _) = self._tocard(0x0C, data)
return recv if stat == self.OK else None
def write(self, addr, data):
buf = [0xA0, addr]
buf += self._crc(buf)
(stat, recv, bits) = self._tocard(0x0C, buf)
if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
stat = self.ERR
else:
buf = []
for i in range(16):
buf.append(data[i])
buf += self._crc(buf)
(stat, recv, bits) = self._tocard(0x0C, buf)
if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
stat = self.ERR
return stat
以下のコードでカードを読み取ります。
from machine import Pin, SPI
from mfrc522 import MFRC522
import time
# SPI 通信の初期化
spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
sck=Pin(18), mosi=Pin(19), miso=Pin(16))
cs = Pin(17, Pin.OUT)
rst = Pin(22, Pin.OUT)
# MFRC522 のインスタンスを作成
reader = MFRC522(spi, cs, rst)
print("カードをかざしてください")
while True:
(status, TagType) = reader.request(reader.REQIDL)
if status == reader.OK:
(status, uid) = reader.anticoll()
if status == reader.OK:
print("カード検出 UID:", uid)
time.sleep(1)
まとめ
RFID-RC522は、Raspberry Pi Picoと組み合わせることで、簡単にICカードの読み取りが可能になります。交通系ICカードと同じ技術を活用して、電子錠や出席管理システムなど、さまざまな応用が可能です。
日常で何気なく使っている技術にふれるのはとても良いですね。わたしは思い切ってモバイルsuicaをかざしてみました。ちゃんと反応しましたよ!(エラーですけど)
コメント