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

出勤できました

ティッシュを消費したらTweetする装置を作った

冬はティッシュの消費量が増える季節です

体調の悪化をなにかしら数値化しておきたい。
鼻をかむ回数が増えるだろうからティッシュの消費量を追えばよさそう。

(あと余談ですが、最近DMM.comの月額動画を契約しました。)

すでにそういう製品があるのでは?

検索してみたけどそういう製品は見当たらない…。
ただ1件、ティッシュとセンサを組み合わせている方を見つけました。

www.jonki.net

そうそう、こういうの。
ここからwebへ何かしらPOSTすればよさそう。

ティッシュの消費量監視システムができました

消費したティッシュの枚数をTwitterにつぶやきます。

後ほどティッシュケースに装着するようにしました。

その結果、こんなTweet↓が流れます。

材料

工作の流れ

  • ティッシュを取り出すと上下するアームをレゴブロックで作る
  • GPIOピンに傾斜スイッチを接続し、傾きによって状態が0,1で変化するのを確認する
  • アームの先端に傾斜スイッチを取り付けて、角度を調整する
  • 取り出し時に、IFTTTのMaker Channel宛てにリクエストを送る
  • IFTTT側でそれをtriggerにして、Twitterへつぶやかせる

それぞれ解説していきます。

ティッシュを取り出すと上下するアームをレゴブロックで作る

試行錯誤しながら、とりあえず動くものを作る時にはレゴブロックが便利。
ギアやプーリーをベースとしたキットが販売されているのでそれを使いました。
http://amzn.to/2kiuv62

デイリーポータルZさんの以下の記事を見ると欲しくなるはず。 portal.nifty.com

今回の工作で作ったパーツは

  • アーム
  • アームを固定するための台座
  • アームが上がりきってしまわないようにするためのつっかえ棒

から成り立っています。

GPIOピンに傾斜スイッチを接続し、傾きによって状態が0,1で変化するのを確認する

以下のようなpythonのコードを書いて「値が0から1に変化したことを検知し、検知後1000ミリ秒間に再度検知しても無視する」ということを実現しています。

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

SWITCH_IO = 26
GPIO.setup(SWITCH_IO, GPIO.IN)

def switch_on(channel):
  # ONに切り替わったので何かする

GPIO.add_event_detect(SWITCH_IO, GPIO.FALLING, switch_on, 1000)

アームの先端に傾斜スイッチを取り付けて、角度を調整する

ティッシュを取り出して十分にアームが持ち上がった時に通電するように、さきほどのアームの先端に角度を調整しつつ傾斜スイッチを設置します。

冒頭の動画を見ると、ティッシュを引き抜いてアームが持ち上がった時にチカッとセンサーのLEDが光っているのがわかるかと思います。

取り出し時に、IFTTTのMaker Channel宛てにリクエストを送る

IFTTTは、各種サービスをtriggerとactionとして扱い、それらを自分好みに組み合わせてタスクの自動化を行えるサービスです。
直接Twitterへ投稿するプログラムをpythonで書いてもよかったのですが、手軽さと柔軟性を考慮してIFTTTを利用することにしました。

IFTTTのMaker Channelを利用することで、webhook URLへのPOSTリクエストをtriggerとして扱えるようになります。 そのための具体的な手順は以下のとおりです。

  1. IFTTTのMaker settingsにアクセス
  2. そこに表示されているURLをコピペしてアクセス
  3. curlで叩く例などが出て来るので、{event}の部分にtrigger名(今回はtissue_takenとしました)を入力して、そのURLをコピー f:id:moyashipan:20170206022114p:plain

そうして得られたURLに対してPOSTリクエストを行うコードを書きます。
こんな感じに↓

import json
import urllib2

data = { 'value1': num_of_tissues, 'value2': first_taken_at.strftime('%Y/%m/%d %H:%M:%S') }
req = urllib2.Request(trigger_url)
req.add_header('Content-Type', 'application/json')

urllib2.urlopen(req, json.dumps(data))

IFTTT側でそれをtriggerにして、Twitterへつぶやかせる

IFTTTに新しいAppletを登録します。
デバッグ中にTwitterに投稿してしまうと迷惑になるので当初は自分にDMを送るようにしました。
If Maker Event "tissue_taken", then send a direct message to @Moyashipan のようなAppletができていて、 ティッシュを取ることで自分にDMが届けば成功です。

そうして全体が上手く連携していることが確認できたら、actionをpost a tweet to @MoyashipanにしたAppletを作り、さきほどのAppletはOFFにします。
そしてティッシュを取ることで自分のアカウントでつぶやきが流れたら完成です。

工夫

複数枚取った後でまとめてtriggerさせる

ティッシュを1枚ずつ取るたびにTwitterへ投稿していては、短時間に複数枚取った場合に迷惑です。
そこで、1枚取ると15秒間のカウントが始まり、その間にさらに取ると15秒プラスされるようにしました。
そしてカウントがゼロになったら、カウント開始時からの枚数をまとめてValue1に格納してtriggerさせています。

こうすることで、たくさん消費する場合には通知までの時間が長引いてしまいますが、 そういう場合にはできるだけまとまった(大きな)数値が投稿されたほうがテンションが上がるだろうということでカウントを追加するようにしました。

問題点

デバッグの後で片付けが必要

水銀スイッチを使っている

傾斜スイッチは、水銀の入ったガラス管が傾き、水銀が導線に触れる事でスイッチがONになるというものです。
人体に有害なため生産されなくなったという物らしいので、他のスイッチを使ったほうが安全そうです。

まとめ

  • ラズパイとスイッチを繋げば、手軽に日常の動作の数値化ができる
  • IFTTTのMaker Channelを使えば、検知した後の動作は全部IFTTTに任せられるのでお手軽

ピエゾブザーで音を出す

ボタンを押した時は以下のような事をしてます。
序盤は「ピ・ピ・ピ…」と鳴って、動画の最後で「ブー」と鳴ったのは、
表示する文字数(cursor)が0の時には、低くて(500hz)長い(0.2秒)音を出しているから。

tone = 500 if cursor == 0 else 1000
delay = 0.2 if cursor == 0 else 0.1
buzzer.ChangeFrequency(tone)

buzzer.start(10)
time.sleep(delay)
buzzer.stop()

音の変化だけでもちょっと生命ある感が出てきてかわいい。

初めの頃、試しに音を流しっぱなしにしながらコードを書いていたら、 音が途切れたり揺れたりしたので「おいおい、そんなに安定した音って出せないもんなのー?」と思ったんですが、
どうやら、ssh接続したラズパイ上のvimでコードを書いていて、カーソルを連続して動かしたような時にそうなるみたい。

CPU負荷とか無線LANとかとの兼ね合いで電圧が変化するんですかね?

スイッチの値が切り替わったことを検知する(add_event_detect)

スイッチの切り替わりをwhileループの中で行おうとすると大変なので、イベントハンドラを設定するという例。

Pythonのコードから一部を抜粋。

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
SWITCH_IO = 26
GPIO.setup(SWITCH_IO, GPIO.IN)

def switch_on(channel):
  print('OFFからONになりました')

GPIO.add_event_detect(SWITCH_IO, GPIO.FALLING, switch_on, 100)

今回の回路は、押していない時にHIGH、押している時にLOWとなる回路なので、GPIO.FALLINGを指定してLOWになった時に検知しています。

100と指定してある値は、ソフトウェアでチャタリングを除去するためですが「10回押したはずなのに10回分検知されなかった」ようなことになるので回路側に工夫するほうがいいんだろうか?

参考

Raspberry Pi 3で16×2 LCDに好きなメッセージを表示する

Raspberry Pi 3を買って遊び始めたので、人にすすめる用にメモっておきます。

遊んでる様子はこちら。

 

今回買ったのは以下の3つ。 

ケースがカッコイイ。 

 

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

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

 

ACアダプタに電源ON/OFFボタンがついてるのと、SDカードにraspbianがインストール済みなのが便利。

 

OSOYOOは日本語が怪しかったけど、手頃な値段(2980円)で色々入ってて良さそうだったのでこちらのキットを買ってみました。

ラズパイからブレッドボードに線をガッサー!!と持っていけるのが便利。

 

以下の記事の通りに進めれば、LCDに文字を表示できます。

http://osoyoo.com/ja/2016/06/01/drive-i2c-lcd-screen-with-raspberry-pi/

 

軽くハマった点としては、別なサイトを参考にしていた時にVCCを3.3vへつなぐように書かれていたのだけど、実際には5vにつなぐのが正しく「裏のつまみ限界までひねっても薄くて見えねーぞオイ」となったことです。アホでした。

 

今後もブログに書くかはわかりませんが、引き続きいじってみます。