はじめに...
課題でfelicaが組み込まれた学生証をタッチすることで入退室の管理システムをPythonで作ることになり、その備忘録的なもので残します...
最終的にはRaspberry piで実行することになりますが、デバッグの関係でMac環境で進めていますが、動作自体は同じなので触れません。
下準備
nfcpyインストール
nfcのライブラリ入れときます。
pip install nfcpy
Mac環境ではこれだけでおしまいなのですが、ラズパイではこのほかにやることがあります。
ラズパイでlsusb
を実行して、リーダーを確認します。
今回使うリーダーはRC-S380
通称"パソリ"なので
Sony Corporation RC-S380/P
ここのID xxx:yyy
は後で要るのでメモっておいた方がいいです。(後々のために)
次に、cd /etc/udev/rules.d/
にアクセスしてtouch nfcdev.rules
を作ってその中に以下のものを保存します。なおxxx
とyyy
は上記で出したものを入れてください。自分は、入力ミスで引っかかってラズパイではしばらく動かすことができなかったなんて言えない
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="xxx", ATTRS{idProduct}=="yyy", GROUP="plugdev"
これでラズパイでも動かすことができるようになりました。
FeliCaをとりあえず読む
ひとまずFeliCaを1秒間隔で、IDm
PMm
システムコード
を出力するものをやってみました。
import nfc
import time
while True:
with nfc.ContactlessFrontend('usb') as m: #felicaリーダーusb扱い
tag = m.connect(rdwr={'on-connect': lambda tag: False}) #felicaカード情報読み込み
print(tag) #情報出力
time.sleep(1) #インターバル
IDm, PMmはカード固有に割り当てられた番号であり、この番号を使えば簡単に実現できるね!
...でおしまいにするのはなんとも楽すぎます。また、学生証をタッチする機会ではいずれも自分の学籍番号が出力されるが、このIDmをサーバーへ送って判定させるには早すぎるんじゃない?と思い、もしかしたらカードにリクエストを投げれば学番を出すことができるのでは?と思い方々調べて見ると、dumpすると学生証の中のデータを出力できるらしいということがわかったのでやってみました。
import nfc
import time
def connected(tag):
# 内容を16進数で出力する
print("dump felica.")
print(' ' + '\n '.join(tag.dump()))
while True:
with nfc.ContactlessFrontend('usb') as m: #felicaリーダーusb扱い
tag = m.connect(rdwr={'on-connect': connected}) #felicaカード情報読み込み
print(tag) #情報出力
time.sleep(1) #インターバル
出力するとこうなります(学番の部分などモザイクつけてます)。
今回、自分が求めたい学籍番号単体で保存されているのがService 128:...(0x2008 0x200B)
の部分です。
ただ、これを取り出すことが途中までできず、そのほかに保存されているService 106:...(0x1A88 0x1A8B)
の0001:~(省略)~|xxxx....|
に他のデータとともに保存されており、そこからはすぐに取れそうだったので、それをやってみました。
学番取り出す 1
import nfc
service_code = 0x1a8b
def connected(tag):
# タグのIDなどを出力する
print(tag)
if isinstance(tag, nfc.tag.tt3.Type3Tag):
try:
# 内容を16進数で出力する
print("dump felica.")
print(' ' + '\n '.join(tag.dump()))
# サービス/ブロックコード指定
sc = nfc.tag.tt3.ServiceCode(service_code >> 6, service_code & 0x3f)
# print("sc: "+ str(sc))
bc = nfc.tag.tt3.BlockCode(0,service=0)
# print("bc: "+str(bc))
# 学籍番号出力
feli_data = tag.read_without_encryption([sc],[bc])
# print(str(feli_data[x:y]))
print("gakuban:" + ''.join(str(feli_data[x:y])))
except Exception as e:
print("error: %s" % e)
else:
print("error: tag isn't Type3Tag")
# タッチ時のハンドラを設定して待機する
clf = nfc.ContactlessFrontend('usb')
clf.connect(rdwr={'on-connect': connected})
上記のソースコードでは、Service 106:...(0x1A88 0x1A8B)
のところの0x1A8B
というサービスコードをまず指定してあげ、それをsc =...
のところで調整してやってます。ここには学番以外のものも含まれているので、取り出したデータをfeli_data[x:y]
とすることで、x, yはそれぞれdumpで出したデータの何番目かを決めてあげます。
まあ、これで出力できても結局誤魔化してる感満載なのと、他の人の学生証で試したところ、若干dumpデータに変化があり、これで出力し続けられない可能性も出てきたので、学番単体のところから出せないかまた試してみました。
学番を取り出す 2
いろいろ調べてみた結果、前述のサービスコード等とは別に、dumpしたデータにあるSystem 809E
というシステムコードも指定してあげる必要があるようで、それらをやってみると以下の通り
import nfc
import time
service_code = 0x200b
def connected(tag):
# 内容を16進数で出力する
print("dump felica.")
print(' ' + '\n '.join(tag.dump()))
#システムコード指定
idm, pmm = tag.polling(system_code=0x809E)
tag.idm, tag.pmm, tag.sys = idm, pmm, 0x809E
global gakuban
if isinstance(tag, nfc.tag.tt3.Type3Tag):
try:
# 学籍番号出力
sc = nfc.tag.tt3.ServiceCode(service_code >> 6, service_code & 0x3f)
print("sc: "+ str(sc))
bc = nfc.tag.tt3.BlockCode(0,service=0)
print("bc: "+str(bc))
feli_data = tag.read_without_encryption([sc],[bc])
print(feli_data[0:8])
gakuban = feli_data[0:8].decode()
# def側学番出力
print("gakuban:" + gakuban)
except Exception as e:
print("error: %s" % e)
else:
print("error: tag isn't Type3Tag")
while True:
with nfc.ContactlessFrontend('usb') as m:
tag = m.connect(rdwr={'on-connect': connected})
# ループ側学番出力
print("main gakunow: "+ gakuban)
time.sleep(1)
これで、見事に単体で出せた!
ただし、データ自体は単体に見えてもバイナリ上若干データを含んでるらしく、プログラム上でもお分かりの通り、切り取った上でデコードしてます。あと、dumpのプログラムを動かしてるとたまにダメになることもあるようで、その時はコメントアウトしてください(テキトーすぎ)。
最終的にできたやつ↓
その後に、ラズパイのGPIOなりIFTTTなりで入退室のアプリ的なの作ったのがこちら
https://github.com/youk720/feligaku_inout/blob/main/main.py
後書き
当初は、課題でこのプログラム作っていたが、ただそれで終わらすのがもったいなくてこの記事書いた次第でしたが、出力結果のスクショをそういえば撮り忘れていたことに後悔...
あと、拙い文章力なのはお許しを...
参考...
- https://qiita.com/zaburo/items/db10ceadd49568d6f6f1
- https://aizu-vr.hatenablog.com/entry/2019/08/02/nfcpy%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%AD%A6%E7%94%9F%E8%A8%BC%E3%81%8B%E3%82%89%E5%AD%A6%E7%B1%8D%E7%95%AA%E5%8F%B7%E3%82%92%E8%AA%AD%E3%81%BF%E5%8F%96%E3%82%8B
- https://qiita.com/koinori/items/ee81e1815de9e4d7f15d