[後半](本当の)0からNFCでカギで操作するソフトを作る。

こんにちは。もりかぷです。
それでは後半戦を始めていきましょう。

前半戦をまだ見ていないかたはこちらから!↓

前半(本当の)0からNFCでカギを操作するソフトを作る。

3.コードを書いていこう

(最後に当サイトで紹介したコードのダウンロード方法を書いておきます。なので、軽く読んで大丈夫です。)

続いて基盤となるプログラム作るためにコーディングしていきます。
その前にどのような動作をするかおおまかに設計します。

まず、今回の目的はNFCでカギを開ける。
つまり事前に登録したNFCチップが読み込まれたらサーボモーターを回したいのです。
また、開けるだけでなく鍵を閉めることもできるようにしたいですね。

これらを軽く整理すると以下の図が完成します。

この図の通りになるように、構築していきたいと思います。

ではどうやってコードを書けば良いのか。
まずはサーボモーターを回すことから始めましょう。
ちなみにここからは、人によって得意なプログラミング言語が変わってくるので、構築言語は変わっていくと思います。

今回はサーボモーターを動作させるに当たって容易なPythonと、システム構築が容易なRubyを用いていきます。

サーボモーターを制御するプログラムを書く前に、一般的なPWM制御について軽く説明させて頂きます。

まずサーボモーターとは、特定の角度で位置決めをする事ができるモーターです。
角度を決めるにはPWM制御(Pulse Width Modulation:パルス幅変調)を用いて角度を決めていきます。
この信号の幅が狭いと0°、幅が大きいと180°のように位置決めが行えます。
この幅はデューティー比によって制御できます。

Raspbianにはこのデューティー比を指定するための関数が、Pythonのライブラリとしてあるため簡単に制御が行えます。なので、サーボモーターの制御にPythonを用います。

途中で周波数を求める部分がありますが、1/PWMサイクル[秒]=周波数[HZ]で求められます。
PWMサイクルはデーターシート(仕様書)などで見られます。

では早速、プログラムを書いていきましょう。尚、プログラムは全て同じフォルダの中に作るようにしてください。
今回はhomeフォルダ(Windowsで言う、自分の名前のフォルダ。)に下にフォルダを作り、プログラムを作成していきます。

という事で先ほどの黒い画面(Terminal)を起動させて、以下のコマンドを入力してください。

mkdir nfckey

このコマンドはMake Directory(フォルダを作成)を略してmkdirとなっています。また、nfckeyという部分はフォルダの名前になります。

mkdirコマンド

mkdir フォルダの名前

このコマンドはLinux(Ubuntu、Arch、Raspbianなどなど)というOSではよく使うので覚えておくといいです。

続いて、このままだと「nfckey」という名前のフォルダを作成しただけなので、プログラムを作成する前にフォルダの中に入る必要があります。
フォルダの移動の際にはcd (change directoryの略)コマンドを使って移動を行います。
このコマンドの使い方は次の通りです。

cd 移動先フォルダ名

なので、先ほどnfckeyというフォルダを作成し、そのフォルダの中に入りたいので、

cd nfckey

というコマンドを実行すると先ほどまで

pi@raspberrypi ~ $

だった表示が

pi@raspberrypi ~/nfckey $

と変化すると思います。これで、nfckeyフォルダの中に入ることができました。
それでは、画面を閉じずコマンドでプログラムを作成してみましょう。万が一閉じてしまったら、Terminalを起動しなおして、cdコマンドでフォルダの中に入りましょう。既にフォルダは作ってあるのでmkdirコマンドは不要です。

次にコマンドでエディタを起動したいと思います。
エディタはvi , vim , emacs , nano , ed …などと挙げればたくさんありますが、今回は操作が分かりやすいnanoというエディタを使っていきます。
勿論、他のエディタが良いという方は使いやすいエディタを使うのが最適解なので、好きなものを使ってください。

起動方法は簡単です。黒い画面に

nano

と入力して実行するだけです。
実行すると下のような画面になると思います。後はここに文字を入力するだけです。

nanoエディタの画面

保存についてですが、ctrl と oキーを同時に押すと下に「File Name to write:」と出るので、ファイル名を入力しEnterキーを押します。

Enterキーを押すと[Wrote (行数) Line]と表示されるので、これで保存完了です。

終了するときは、ctrlとxキーを同時に押しましょう。
すると、いつも見るpi@~~の画面に戻れるはずです。

それでは、試しに以下のコードを入力していきましょう

ちなみに#はコメント行なので#が先頭についている行は書かなくても大丈夫です。また、半角空白8文字はtabキー1回で行なってください。

ファイル名: lock.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#ライブラリの読み込み
import time
import RPi.GPIO as GPIO

#おまじない
GPIO.setmode(GPIO.BCM)

#gpionumという変数(箱のようなもの)に4を代入
gpionum = 4

#gpionumの数字のGPIOを出力に設定(ここではGPIO4)
GPIO.setup(gpionum, GPIO.OUT)

#サーボモーターSG5010はPWMサイクル20ms(0.02秒)、より50Hz、なので(GPIOの番号, 50)とする
servoctl = GPIO.PWM(gpionum, 50)

#サーボモーター動作開始
servoctl.start(0.0)

#デューティー比を変更
servoctl.ChangeDutyCycle(12)
#0.5秒ウェイト
time.sleep(0.5)

#GPIO設定を初期化
GPIO.cleanup()

ファイル名: unlock.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#ライブラリの読み込み
import time
import RPi.GPIO as GPIO

#おまじない
GPIO.setmode(GPIO.BCM)

#gpionumという変数(箱のようなもの)に4を代入
gpionum = 4

#gpionumの数字のGPIOを出力に設定(ここではGPIO4)
GPIO.setup(gpionum, GPIO.OUT)

#サーボモーターSG5010はPWMサイクル20ms(0.02秒)、より50Hz、なので(GPIOの番号, 50)とする
servoctl = GPIO.PWM(gpionum, 50)

#サーボモーター動作開始
servoctl.start(0.0)

#デューティー比を変更
servoctl.ChangeDutyCycle(8)

#0.5秒ウェイト
time.sleep(0.5)

#GPIO設定を初期化
GPIO.cleanup()

こんな感じになりました。
非常に少ない行数でサーボモーターを制御することができます。この行数で済むのもライブラリのおかげです。
この2つのファイルの違いについてですが、デューティー比変更の際に12としているか8としているかの違いだけです。
この値は後ほど調整するので、実際に設置してから値を変更することになります。

次にNFCのIDを取得するためのプログラムを作成します。
ここで「2.ライブラリをインストールしよう」でインストールしたnfcpyを使っていきます!

では早速コードを以下に示します。

ファイル名: cardload.py (今回のイレブンバンド、Type2規格のカードに使用)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#nfcpyライブラリの読み込み
import nfc

#NFC読み込み
def connected(tag):
        print tag
clf = nfc.ContactlessFrontend('usb')
clf.connect(rdwr={'on-connect': connected})

ファイル名: felicaload.py (交通系ICカード、Type-F規格(Felica)のカードに使用)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#各種ライブラリの読み込み
import nfc, nfc.clf
import binascii

#NFC読み込み
def on_connect(tag):
    print tag
def main():
    clf = nfc.ContactlessFrontend("usb")

    rdwr_options = {
        'targets': ['212F' , '424F'], #Felicaの通信速度である212Kbps,424Kbpsをターゲットにする
        'on-connect': on_connect,
    }
    clf.connect(rdwr=rdwr_options)

if __name__ == '__main__':
    main()

では次に先程の図の順序通りに動作するように、基盤となるソフトを作ります。
これはPythonではなく、Rubyを使います。理由は自分が作りやすかったからです。
ファイル名: system.rb

# -*- encoding: UTF-8 -*-
#ログ記録の際に時刻を取得する必要があるためtimeライブラリ読み込み
require 'time'

#NFC IDデーターベースの定義
DB = {"YourName1" => "XXXXXXXXXXXXXXX",
        "YourName2" => "XXXXXXXXXXXXXXX" }

#NFCカード読み込み用プログラムの起動宣言
def cardload()
    `sudo python cardload.py`
end

#解錠用サーボモーター動作プログラムの起動宣言
def unlock
    `sudo python unlock.py`
end

#施錠用サーボモーター動作プログラムの起動宣言
def lock
    `sudo python lock.py`
end

#cardload関数で得られた出力からID部分を正規表現で得て、返す関数
def id(outid)
        matchcode = outid.match(/ID=(.*?)\s/)
        myid = matchcode[1]
        return myid
#想定外の規格のカードが来た時の例外処理
rescue
    error = '000000000'
    return error
end

#ループ処理
loop do
    print("Locker>>Waiting Card...\n")
    getid = id(cardload)
    username = DB.key(getid)

#データーベース上とIDが一致した場合
    unless username == nil
        file = File.open('log.txt','a') #ログファイル展開
        print("Locker>>Auth OK: #{username} \n") #メッセージ出力
        timestanp = DateTime.now #現在の日付、時刻を取得
    unlock #解錠
        file.puts "#{timestanp}  Unlock  #{username}" #ログファイルに日付、時刻、操作内容、登録名を書き込み
        file.close #ログファイルを閉じる
        sleep(10) #10秒間待機
    lock #施錠
        system("clear") #画面リセット
#データーベース上とIDが一致しなかった場合
    else
        print("Locker>>Auth NG: Unregistred ID\n") #メッセージ出力
    sleep(2) #2秒待機
        system("clear") #画面リセット
    end

end

ただし、Felicaを用いる場合、cardload()の中のcardload.pyを以下になるよう

#NFCカード読み込み用プログラムの起動宣言
def cardload()
    `sudo python felicaload.py`
end

に書き換えて下さい。
これで動作させるのに必要なプログラムが完成しました。
それでは、次回「4. 動かそう!」で実際に動作させてみたいと思います。

今回作成したプログラムは次のコマンドでダウンロードすることができますので、ご自由にお使いください。

cd ~
git clone https://github.com/tmorio/elevenband_nfckey.git
cd elevenband_nfckey

この方法でダウンロードした場合、ディレクトリがelevenband_nfckeyとなりますのでご注意下さい。
また、GitHubページを出しておきます。

GitHub – tmorio/elevenband_nfckey

4. 動かそう!

みなさんが好きな動作確認の時間です。 まずサーボモーターが動作することを確認します。 サーボモーターとRaspberry Piの接続方法についてですが、GPIO(General Purpose Input Output)ピンに接続します。 接続の前にサーボモーターの線を見てみて下さい。 今回の場合、茶色、赤色、黄色の3色で構成されていますね。 これらの色についてですが、以下の構成となっております。 [su_table]

役割
茶色GND
赤色電源(Vcc)
黄色信号線

[/su_table] また、Raspberry Pi 3 のGPIO構成は以下の画像の通りとなっております。(クリックで拡大できますよ〜)

GPIO配置

今回、lock.pyとunlock.pyではGPIO 4を信号線としています。また、今回用意したSG5010というサーボモーターは電源電圧4.8V ~ 6.0Vで動作するので、電源は「5v DC出力」のピンに挿すと良さそうですね。なので次のように接続します。

各色の対応

では、早速接続…といきたいところですが、3列並べないと接続ができないので「ジャンパー線(オス – メス)」を使って接続します。
ジャンパー線とは延長コードのようなものです。

接続は外れないようにしっかりとね!

これで、好きなところに接続できるようになるので、先ほどの画像のように接続します。
色については元線が対応するように接続します。
また、短絡しないようにしっかりピンに挿しましょう。
自分はピンがうまく挿さっていなかったのか不明ですが、これでジャンパー線溶かしました。危ない危ない…

図では5V電源とGNDを隣合わせに接続していますが、以下の方が良いかもしれません。

こっちの方が良いかも
こちらもしっかり!

これでサーボモーターとRaspberry Piとの接続は完了です。 それではプログラムを起動させて、実際にサーボモーターを動かしてみましょう。 動作方法は簡単です。作ったプログラムと同じフォルダにいる状態で以下のコマンドを実行します。

python lock.py
python unlock.py

動作しましたか?多分以下のように動くと思います。

万が一動かない場合、コードが正しく書かれているか、配線が正しいかを確認してみて下さい。
なお、サーボモーターに何かついていますが、これはサーボモーターを購入するときについてきた部品です。
必要に応じて付属のパーツとねじを使ってサーボモーターに固定して下さい。

また、このコマンドについてですが、Pythonを実行するコマンドです。

python 実行したいファイル

では、サーボモーターの動作確認ができたので続いて、NFCの読み込みを行ってみたいと思います。
それでは、Raspberry PiのUSBポートにカードリーダーを接続して下さい。

NFCリーダーライターを接続しよう

NFCの読み込みについてですが、読み込みたいカードの種類のよって実行するコードが違います。
コーディングの際にType2とFelicaの種類のプログラムを作成しました。
自分は今回、イレブンバンド(Type2のNFCチップ)を用意しました。

イレブンバンド。今回はこれを鍵の代わりにする。

Type2の為、使用するコードはcardload.pyなので、

sudo python cardload.py

となります。

ですが、SuicaやPasmoなどの交通系ICカード、おサイフケータイ、nanacoなどのFelicaを用いる場合は、felicaload.pyを使用する為

sudo python felicaload.py

となります。

sudoを使う理由はカードリーダーとの接続の際に、root権限が必要だからです。root権限を不要にする方法もありますが割愛します。

それでは実際にコマンドを実行して、カードリーダーに読み込みたいカード・チップをかざしてみましょう。

実行後、「No handlers cloud be found for logger “nfc.llcp.sec”だけ出たら読み込み準備OK
かざしてみる

すると図のように何か画面上に文字列が出力されると思います。この文字列が重要な鍵となります。
表示例ですが

Type2Tag ID=00000000000000
00000000000000

と表示されると思います。(felicaload.pyの場合は多少変わりますが、同じくID=…が出ます。)
この00000000000000の部分がNFCの固有番号です。とりあえずこの番号をメモしてください。
また、この番号は他人に知られないようにして下さい。

次の今回かざしたカードの情報をプログラムに埋め込みます。

nano system.rb

でsystem.rbを開いてください。
ファイルを開くと、nanoエディタ上に先ほど書いたプログラムが出ると思います。


それではカード情報を埋め込んでいきましょう。
埋め込むには今回の場合、DBという名前の配列に書き込む必要があります。
まずは、矢印キーでDBが書かれている行までカーソルを移動してみましょう。
そしたら、YourName1の部分をログに記録したい名前、XXXXの部分を先ほどメモしたNFCの固有番号に置き換えてください。(以下に例を示します。)

例) 名前がAsutoInamori、NFC固有番号が0000000000000の場合:

system.rb 編集前

#NFC IDデーターベースの定義
DB = {"YourName1" => "XXXXXXXXXXXXXXX",
        "YourName2" => "XXXXXXXXXXXXXXX" }

system.rb 編集後

#NFC IDデーターベースの定義
DB = {"AsutoInamori" => "0000000000000",
        "YourName2" => "XXXXXXXXXXXXXXX" }

編集できましたか?
編集出来たらctrlとoキーを同時に押し、そのままEnterキーを押してファイルを上書き保存します。
これでカード情報の埋め込みが完了です。
それではctrlとxキーを押して終了しましょう。

では、ソフトの動作確認を行います。
サーボモーターとNFCリーダーライターを接続した状態で進めていきます。

system.rbはRubyという言語で書いたため、Rubyコマンドでプログラムを実行します。

ruby 実行したいファイル名

という事で、ファイル名がsystem.rbであるため

ruby system.rb

を実行しましょう。
実行すると

locker>>Waiting Card...

と表示されるので、先ほどかざしたカードをかざしてみましょう。
かざしてみると、サーボモーターが回ります!
また、10秒後に逆回転します。
この後、またWaitng Card…と表示されればプログラムは正常に動作しています。
なお、別のカードをかざすと、サーボモーターは動きません。

DBにある固有番号かどうかで判定をしているため、登録したカード以外では反応しません。

以下に、僕が製作した一例を載せておきます。

動画では色々表示されていますが、少し改変してあるので同じ表示じゃなくても大丈夫です。
プログラムを終了する際はctrlとcキーを同時に押すと終了できます。
重要なのは、登録したNFCでのみサーボモーターが動き、10秒後に元に戻る、未登録のNFCがかざされたときは反応しないことです。

うまく動作しないなどがあれば、NFCの固有番号の入力に問題があることが多いです。
読み取ったNFCの固有番号と、system.rbに入力した固有番号を再度確認してみましょう。

問題なく動作したら動作確認を完了です。お疲れ様でした!
続いて、「5. 設置しよう!」に進みましょう。

5. 設置しよう!

今回作ったシステムを実際に設置していきます。
注意点としては、NFCカードとNFCリーダーライターの間に金属板があると正常にカードが読み取れなくなってしますので注意してください。
必要に応じて、アンテナの製作やNFCリーダーライターを外に出してください。

僕の場合ロッカーに僅かな隙間があるので、カードは無理そうですが、小型のチップなら読み取れそうです。

僅かな隙間が・・・

では設置します。鍵の仕組み応じてサーボモーターの設置を行って下さい。
自分はロックの際に鍵が押し出されるように設置しました。

設置例

固定方法についてですが、環境によって変わってくるので各自固定してください。

また、もしもサーボモーターの故障時や、モバイルバッテリーでの駆動時にバッテリー切れになる場合があるため、普通のカギでも開けられるように設置しておきましょう。(開かなくなります)
なので、ロッカーに設置するのではなく、ドアにある以下のような鍵に軽く設置する程度にしましょう。

鍵の一例 ここにサーボモーターが軽く引っかかるようにつける

設置完了したらsystem.rbを起動して、動かしてみましょう。
僕の動作例を以下に載せておきます。

もしも、サーボモーターの角度が合わないなと思ったら、lock.pyとunlock.pyをエディタで開き、

#デューティー比を変更
servoctl.ChangeDutyCycle(8)
#0.5秒ウェイト
time.sleep(0.5)

servoctl.ChangeDutyCycle(8)

の数字を変えて調整してみましょう。0~12の範囲で設定します。

うまい具合に調整後、先ほどの重要ポイントである、登録したNFCでのみサーボモーターが動き、10秒後に元に戻る、未登録のNFCがかざされたときは反応しないことの3つの条件が満たせれば完成です。

これで終わりです。お疲れ様でした!

何度も注意しますが、必ず普通のカギでも開けられるようにしてください。

設置不良で開けられなくなっても、責任は負いかねます。

あと前半でも言いましたが、カードエミュレーションでIDmを偽装できるので、時間を掛ければ認証突破できてしまうので注意しましょう。(かなり時間がかかるのでやる人はあまり居ないと思いますが…念の為)

要するに家の鍵など重要な部分には付けないようにしましょう。

実際に家の鍵など安全性を要求する所に設置するならば、Felicaのブランクカード買ってメモリ領域に適当な値を書き込み、IDm認証+メモリ領域認証を実装すれば不正に突破される確率は少なくなります。(メモリも偽装されれば突破はできてしまうけど、それだけパターンは増えるので更に時間がかかると思われます。ただ、絶対に突破できなくなる訳では無いので注意。)

(実際の製品とかはどうなっているのか、良く調べていないので分かりませんが…)

終わりに

ということで今回は0からカードキーシステムを作ってみました。
いかがでしたでしょうか。今回は簡易的に製作を行いましたが、実際の製品にするにはもう少し改良とセキュリティの強化を行う必要があります。
もし、今回製作していて、おもしろいと感じてみたらインターネット上に多くの文献があるので、自分で調べて改良するのもおすすめです。

また、Raspberry Piはこれ以外にも様々な工作に応用できるので、ぜひ自身のアイデアを積極的に実際のカタチにしてみてください。

稚拙な文章にもかかわらず、ここまで読んでいただきありがとうございました。
何かご質問やご指摘、感想が御座いましたら、当記事のコメント欄までお願い致します。

ありがとうございました。

参考文献

Getting started — nfcpy 0.13.5 documentation
Raspberry Pi 3 Model B+ GPIO 40 Pin Block & PoE Header Pinout – Element14 Community
Download NOOBS for Raspberry Pi
Sony Japan | FeliCa | FeliCaとは | FeliCaって何?
Raspberry Pi – Wikipedia
nfcpy で複数の System Code を持つ NFC タグを扱う方法 – uchan note
Raspberry Pi に nfcpy をインストールする手順 – 2016年3月 私家版
Raspberry Piの初期設定まとめ – OUTPUT48
玄関をSuicaで開ける – ゆっくり技術ノート!
Raspberry Piでサーボモーターを回す – Quiita

コメントやツッコミをどうぞ。