ポタージュを垂れ流す。

マイペースこうしん(主に旅行)

学生証で入退室管理するシステムを作った

最近縁があってある研究室に出入りしているのですが、コロナのことも考えて入退室の記録をとることになりました。はじめは紙に入退室時間を書くとかいうアナログな方法で記録を取っていたのですが面倒...。そこで課題がひと段落ついたところで*1気分転換(?)がてらちょっと工作しようということで入退室管理システムをサクッと作りました。先駆者がいたので意外とすぐに作れた。プログラム自体は半日くらい?

追記:GitHub

github.com

f:id:potaxyz:20201214234434j:plain
学生証タッチ

(うしろはモザイクでかくした)raspberry piにしたのリンクに貼るようなカード読む端末をさしています。

機能とか

学生証読み取り部分

「nfcpy 学生証」とかで調べると先駆者の記事が見つかるので、それを参考に学生証の欲しい情報(ここでは学籍番号と名前、あとは興味でチャージの残高*2)を取ります。学籍番号しか取れないと思ってたけど名前も格納されてるんですね。

あとは気になったのでSuicaの残高を取るやつも作った。

import nfc
import struct

# 京都大学学生証 学籍番号と名前(半角カナ)を返す
def read_kucard(tag):
    servc = 0x1A8B
    service_code = [nfc.tag.tt3.ServiceCode(servc >> 6, servc & 0x3F)]

    tag.dump() # これがないと何故かうまくいかなかった

    bc_id = [nfc.tag.tt3.BlockCode(0)]
    bd_id = tag.read_without_encryption(service_code, bc_id)
    student_id = int(bd_id[2:-4].decode("utf-8"))

    bc_name = [nfc.tag.tt3.BlockCode(1)]
    student_name = (
        tag.read_without_encryption(service_code, bc_name)
        .decode("shift-jis")
        .rstrip("\x00")
    )

    return student_id, student_name

# 交通系ICカード(モバイルでない)から残高を返す
def read_suica(tag):
    servc = 0x090F
    service_code = [nfc.tag.tt3.ServiceCode(servc >> 6, servc & 0x3F)]
    block_code = [nfc.tag.tt3.BlockCode(0, service=0)]
    row_le = struct.unpack(
        "<2B2H4BH4B", tag.read_without_encryption(service_code, block_code)
    )
    balance = row_le[8]

    return balance

nfcpyで学生証から情報を読む+おまけ · GitHub *3

学生証がかざされるとデータベースの履歴テーブルに日時と学籍番号と名前と入室or退室の情報が書き込まれる。入室or退室は履歴テーブルとは別に作った在室テーブルにその学籍番号が存在しているかどうかで判断して記録することにした。

管理ページ

コマンドラインからsqlite叩くのもアレだし、webアプリで操作できる感じにしようということでfastapiを使って作ってみた。raspberry piが繋がってるのと同一のネットワーク内ならraspberry piipアドレスからアクセスできるようにした。いままでflaskとresponderをさわったことはあったけど初めて(fastapiを選んだ理由は特になく別のものを触ってみようと思ったから)。似た感じで作れたかな。あんまりapiの良さ(?)を生かした感じにはできなかったけど。

入退室の履歴は見たいのでそれは表示するとして、いざ何かあったというときに履歴を何らかのファイルとして見たいはずなので、データベースに登録されているデータはcsv形式でダウンロードできるようにした。

あとは間違って二重タッチしちゃったとか退室のときにタッチし忘れたとかのために、入退室の情報が手動でデータベースに追加/削除できるようにはした。このへんはもう少ししっかり作ってもいいかもしれない。

f:id:potaxyz:20201215001404p:plain
履歴の表示
f:id:potaxyz:20201215001506p:plain
データの修正ページ 削除かレコードの追加はできる
f:id:potaxyz:20201215001607p:plain
あとから履歴の追加

*4

Slackに投稿

Slackに入退室情報を流すのに適当にslack appを入れて適当に走らせた。Suicaの残高読み取りの機能せっかく作ったのに使わないのもアレだと思って学生証じゃなくてSuica載せて遊ぶやつがいたら残高をSlackに流す仕様にした。

f:id:potaxyz:20201215002508j:plain
slackへの通知

得た知見

  • 今までraspberry piに接続してvimで色々いじったりとかscpでデータ送ってたりしたけど、vscodeから普通のパソコンのように操作できることを知った。Visual Studio Code Remote Development Extension Packとかいうやつ。

marketplace.visualstudio.com

  • はじめてコードフォーマッタを使った。blackを使ってみた。いいですね、これから使っていこうと思った。
  • raspberry pi内の自動起動の設定。今までcron使ってたけどはじめてsystemdを使った。設定めっちゃ簡単やな。
  • ipアドレス固定のあたりでルーターのipが10.0.〜ならstatic ip addressのとこも10.0.〜みたいにしないと1回つながるかもしれんけど切れる。10.0.〜と192.168.〜とかはダメ。

*1:実際には実験を放置してこっちのプログラムを書いていた

*2:たしかtagPurse Serviceのとこの頭2バイト分のとこにカード残高が格納されていたはず

*3:github gistそのまま貼ろうとしたらhtmlタグ展開されて貼れんかった なんで

*4:ブログに載せるためにxxxxxxxx研究室とかにしてhtmlいじった