FeliCaをタイムカードにする装置を自作する

きっかけ: 他社ではFeliCaをタイムカードにしているらしい

友人の話を聞いていたところ、どうやらFeliCaをタイムカードとして使う出退勤システムがあって、それを導入している会社がけっこうあるらしい。
たしかにググるといろいろ出て来る。

felica タイムカード - Google 検索 f:id:moyashipan:20170303000238p:plain

だけどうちの会社の出退勤システムは内製で、FeliCaには対応していない。

しかし、ビルの出入り用に配られているセキュリティーカードがどうやらFeliCa(正確には、なにかしらのNFCタグ)らしい。 (リーダーにかざす時にSuicaなどが近いと上手く動かなかったりすることからわかった)

そして、うちの出退勤システムは特にめんどうな認証などが無く、社内ネットワークからPOSTリクエストを送るだけで良い。

というわけでFeliCaで出勤する装置を作った

材料

Raspberry Pi 3

Raspberry Pi 3の電源とか

Raspberry Piスターターパック (Economy) - Pi3検証済

Raspberry Piスターターパック (Economy) - Pi3検証済

PaSoRi RC-S380

ソニー SONY 非接触ICカードリーダー/ライター PaSoRi RC-S380 : RC-S380

ソニー SONY 非接触ICカードリーダー/ライター PaSoRi RC-S380 : RC-S380

・ピエゾブザー

・ブレッドボードとか

作り方

こちらを参考に、ラズパイをセットアップしたり、nfcpyをインストールしたりします。
Raspberry PiでFelicaのIDmを表示する - Qiita

あとは、タッチした時にカードのIDm(固有ID)からユーザーを判別して、それにあった情報をPOSTするだけ。
FeliCa IDmとは? - 技術者向けNFC(FeliCa,Mifare)開発支援のNFC Developers'サイト

まぁ現状のユーザーは自分だけなんですが…。
コードはだいたいこんな感じ。

# -*- coding: utf-8 -*-

import binascii
import nfc
import time
import json
import requests
import RPi.GPIO as GPIO

class MyCardReader(object):
    # POST先
    trigger_url = 'http://YOUR_TIMECARD_SYSTEM_URL/'
    # カードのIDmとユーザーの対応表
    ids = {
        '0000111122223333' : { 'name': 'moyashipan' }
    }
    # ブザーが接続されたGPIOピン
    pin = 10
    buzzer = None

    def __init__(self):
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.pin, GPIO.OUT)
        self.buzzer = GPIO.PWM(self.pin, 1000)

    def on_connect(self, tag):
        print "touched"
        print tag
        # Type3のタグだけを受け付ける
        if isinstance(tag, nfc.tag.tt3.Type3Tag):
            self.idm = binascii.hexlify(tag.idm)
            self.post(self.idm)
        return True

    def post(self, id):
        if self.ids.has_key(id):
            user = self.ids[id]
            print user['name']
            
            # POSTする
            data = {
                'user_name': user['name']
            }
            res = requests.post(self.trigger_url, json=data)

            print(res)

            # 成功したらピー。失敗したらブーと鳴らす
            tone = 1000 if res.status_code == requests.codes.ok else 300
            self.buzzer.ChangeFrequency(tone)
            self.buzzer.start(10)
            time.sleep(0.2)
            self.buzzer.stop()
        else:
            print '未知のタグです'

    def read_id(self):
        clf = nfc.ContactlessFrontend('usb')
        try:
            clf.connect(rdwr={'on-connect': self.on_connect})
        finally:
            clf.close()

if __name__ == '__main__':
    try:
        cr = MyCardReader()
        while True:
            print "touch card:"
            cr.read_id()
            print "released"
    # ここらへんよくわかってない
    except KeyboardInterrupt:
        pass

出勤できました