【改訂版】Suicaをカードリーダで認識させて、サーボモータを回すスマートロックもどきを作った。

### 【2019/05/18 追記】
- **DBをSQLiteから、MariaDBに変更しました。**
- **旧ソースコードでは、複数カード登録した際にタイムラグが発生する現象が起きていたので、修正しました。**

# はじめに

ひょんなことから、スマートロックのプロトタイプ開発をすることになり、色々ネットで記事を漁りつつ、開発をしてみました。

参考になった記事を、敬意を評しつつ、ここに載せておきます。
基本的な事はこちらに書いてますので、見てください。
[Raspberry Piでサーボモーターを回す](https://qiita.com/undo0530/items/51cc446eefec6c91c26d)
[Raspberry PiにNFCリーダを接続してSuicaを読み取る](https://qiita.com/undo0530/items/89540a03252e2d8f291b)
[Raspberry Piとサーボモータで踊るはにわ制作](https://dotstud.io/blog/dancing-haniwa-servo-raspi/)
[Raspberry PiでPythonからMySQLデータベースを扱う](https://make-muda.net/2016/07/3550/)

# 必要なもの

### 物理的なもの
Raspberry Pi 3 B+(今回はスターターキットを購入)
サーボモータ
ジャンパーピン(**オス/メス**)
USBキーボード
USBマウス
ディスプレイ
NFCカードリーダ([PaSoRi RC-S380](https://www.amazon.co.jp/%E3%82%BD%E3%83%8B%E3%83%BC-SONY-%E9%9D%9E%E6%8E%A5%E8%A7%A6IC%E3%82%AB%E3%83%BC%E3%83%89%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC-PaSoRi-RC-S380/dp/B00948CGAG/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=Y2LW7B5BIWLS&keywords=nfc%E3%82%AB%E3%83%BC%E3%83%89%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC&qid=1557914867&s=gateway&sprefix=nfc%E3%82%AB%E3%83%BC%E3%83%89%2Caps%2C236&sr=8-3))

### 開発環境
OS: Raspbian
言語: Python2
DB: MariaDB(MySQL)



# やりたいこと
- 事前に、DB登録プログラムを動かして、DBにカードのIDmを登録。
- スマートロックもどきプログラムを動かして、カードを認証
- 認証されたカードが、DBにあるIDmと一致した場合、サーボモータが回転する。
- 今回は、Suicaと白無地のFelicaカードを認証するプログラムを書く。


<img width="400" alt="スクリーンショット 2019-05-15 19.15.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154610/5936d5b3-25bc-df7c-e061-36e28de88a7a.png">


# DB登録追加プログラムを作成
まず、以下のコマンドを実行して、nfcpyをインストールする。

```
pip install nfcpy
```

そして、MariaDBにデータベースを作成して、テーブルを作成する。

```
create database suica;
use suica;
create table usercard(id int(11) primary key, auto_increment, card varchar(100));
```

そして、DB登録プログラムを作成する。
以下を記述。

```python:add_suica.py
import binascii
import nfc
import time
import pymysql.cursors
import hashlib
from threading import Thread, Timer
from contextlib import closing
from mysql.connector import errorcode

TIME_cycle = 1.0
TIME_interval = 0.1
TIME_wait = 3
count = 0
#suica
target_req_suica = nfc.clf.RemoteTarget("212F")
#それ以外のカード
target_req_blank = nfc.clf.RemoteTarget("212F")

target_req_suica.sensf_req = bytearray.fromhex("0000030000")

connection = pymysql.connect(host="localhost", user="root", password="********", db="suica", charset="utf8")

print 'Suica waiting...'


while True:
    clf = nfc.ContactlessFrontend('usb')
    # suica
    target_res = clf.sense(target_req_suica, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)
    #それ以外のカード
    target_res2 = clf.sense(target_req_blank, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)

    #suicaかそれ以外のカードか分別する
    target = None
    if target_res != None:
        target = target_res
    elif target_res2 != None:
        target = target_res2

    if target != None:
        tag = nfc.tag.activate(clf, target)
        tag.sys = 3
        
        idm = binascii.hexlify(tag.idm)  
        
        with connection.cursor() as conn:
            hash_idm = hashlib.md5(str(idm)).hexdigest()
            insert = 'insert into usercard (card) values (%r);' % hash_idm
            print insert
            conn.execute(insert)
        connection.commit()
        time.sleep(10)
    clf.close()
```

# 物理的な設定(サーボモータ)

**オス/メス**のジャンパーピンも併用して、サーボモータに接続する。
サーボモータのケーブルと同色のジャンパーピンを接続し、
ジャンパーピンを以下に記載しているラズパイのピンに接続する。

赤: 5V
茶: GND
オレンジ: GPIO系で任意のもの(今回は**GPIO4(端子番号7番)**に接続)

# スマートロックもどきプログラムを作成する。
いよいよ、スマートロックもどきライブラリを作成する。

```python:suica.py
import binascii
import nfc
import RPi.GPIO as GPIO
import time
import pymysql.cursors
import hashlib
from threading import Thread, Timer
from contextlib import closing

TIME_cycle = 1.0
TIME_interval = 0.1
TIME_wait = 1
status = 0
count = 0
target = ''

def open():
    GPIO.setmode(GPIO.BCM)
    gp_out = 4
    GPIO.setup(gp_out, GPIO.OUT)
    servo = GPIO.PWM(gp_out, 50)
    servo.start(0)
    servo.ChangeDutyCycle(12)
    time.sleep(0.1) 
    servo.stop()

def close():
    GPIO.setmode(GPIO.BCM)
    gp_out = 4
    GPIO.setup(gp_out, GPIO.OUT)
    servo = GPIO.PWM(gp_out, 50)
    servo.start(0)
    servo.ChangeDutyCycle(7.25)
    time.sleep(0.1)
    servo.stop()

target_req_suica = nfc.clf.RemoteTarget("212F")
target_req_blank = nfc.clf.RemoteTarget("212F")

target_req_suica.sensf_req = bytearray.fromhex("0000030000")

connection = pymysql.connect(host="localhost", user="root", password="mnmr0043", db="suica", charset="utf8")

print 'Suica servo waiting...'

while True:
    clf = nfc.ContactlessFrontend('usb')
    target_res = clf.sense(target_req_suica, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)
    target_res2 = clf.sense(target_req_blank, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)
    #print target_res
    target = None
    if target_res != None:
        target = target_res
    elif target_res2 != None:
        target = target_res2

    if target != None:
        tag = nfc.tag.activate(clf, target)
        tag.sys = 3
        
        idm = binascii.hexlify(tag.idm)
        #print idm 
        print 'sleep' + str(TIME_wait) + 'seconds'

        with connection.cursor() as conn:
            c = conn
            db_idm = hashlib.md5(str(idm)).hexdigest()
            select = 'select card from usercard where card=%r;' % db_idm
            c.execute(select)
            result = c.fetchall()
            for row in result:
                if db_idm == str(row[0]):
                    if status == 0:
                        open()
                        print 'servo turn'
                        print status
                        status = 1
                        count+=1
                    else:  
                        close()
                        print 'servo reverse turn'
                        print status
                        status = 0
                    time.sleep(TIME_wait)
                    print 'sleep' + str(TIME_wait) + 'seconds'
                else:
                    print 'Not users Auth.'
    
    GPIO.cleanup()   
    clf.close()
```

# 完成

最初に、以下のコマンドでDB登録ライブラリを動かして、カードを読み込む。

```
sudo python2 add_suica.py
```

次に、以下コマンドでスマートロックもどきライブラリを動かして、カードを読み込むと、サーボモータが動く。

```
sudo python2 suica.py
```

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Raspberry Piでスマートロックもどき作った。<br>・DBにカードのIDmを登録するライブラリを動かして、カードを認識させて登録。<br>・認証したカードのIDmが、DBにあるIDmと一致した場合、サーボモーターが回る。(ない場合は回らない。) <a href="https://t.co/Aa8MHCZjSD">pic.twitter.com/Aa8MHCZjSD</a></p>&mdash; やっぴー@20卒 (@yanashi_masa) <a href="https://twitter.com/yanashi_masa/status/1128598604203970561?ref_src=twsrc%5Etfw">2019年5月15日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 感想
・意外と簡単だった。
・ここから、電子工作で拡張していくので、結構大変になるかも。。。