ラズパイでスマートロック作ってみた④ – ICカードでドアを開閉する |じょるブログ

じょるブログ

電子工作やプログラミング関連の情報を発信している技術系ブログ

電子工作 ラズベリーパイ

ラズパイでスマートロック作ってみた④ – ICカードでドアを開閉する

投稿日:2019年8月5日 更新日:

大学生の電子工作 ラズパイでスマートロック作ってみたの記事の④つ目の記事です。今回はSuicaや学生証、電子マネー(nanacoとか)等のICカードで鍵を開けられるようにしました。

近くのコンビニに買い物に行くときなどは、財布だけをもってスマホを持たずに外出してしまうことが何度かありました。(そのようなときは研究室のパソコンから自宅のラズパイにVNCで直接アクセスし鍵を開けました。)

現状私の家の玄関を開けるには、

などの方法がありますが、財布のみをもって外出してしまった場合、鍵を開ける手段がありません。

スマホも財布も持たずに外出することは考えられないので、今回は財布に入っているICカードで開けられるようにし、スマートロック完全体を作成してスマートロックに締め出されないようにしようと思います!

また、スマホの手帳型ケースにもICカードが入っているので、スマホを操作せずともスマホをタッチするだけで鍵が開けられるようにもなります。

     

搭載した機能

  

実行環境

  • ラズベリーパイ3 B+
  • ICカードリーダー ( RC-S320 )

Raspberry Pi4 ModelB 4GB ラズベリーパイ4 技適対応品

SONY RC-S320 非接触ICカードリーダ/ライタ PaSoRi 「パソリ」

      

手順

  1. ICカードを読み取るための基本設定
  2. ICカードで鍵を開けるプログラムの作成
  3. スプレッドシートへのICカードの記載

  

①ICカードを読み取るための基本設定

まず、ICカードリーダーを使用してICカードを読み取れるようにする設定を行います。

この設定方法については以下の記事をご覧ください。

上記の記事内“実装方法” の項目のところまで行ってください。

  

②ICカードで鍵を開けるプログラムの作成

次に、以下のプログラムを作成し保存してください。

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

import paho.mqtt.client as mqtt
import subprocess
import datetime
import json
import sys
import time
import RPi.GPIO as GPIO
from time import sleep
from ctypes import *
import concurrent.futures



##### beebotteのセッティング #####
HOST = 'mqtt.beebotte.com'
PORT = 8883
CA_CERTS = 'mqtt.beebotte.com.pem'
TOKEN = 'token_xxxxxxxxxxx'
TOPIC = 'xxxxxxxx/xxxxxxxx'


# libpafe設定
FELICA_POLLING_ANY = 0xffff
allow_idm_list = ['1234567890123456', '112233445566aabbb']


##### サーボモーターのセッティング #####
# pinの指定方法をBCMに設定(GPIO.BOARDとすると基盤のピン番号となる)
GPIO.setmode(GPIO.BCM)
# pin番号の指定
pin_servo = 4
# 指定したpinを出力に設定
GPIO.setup(pin_servo, GPIO.OUT)
# PWMサイクルを50Hz(20ms)に設定 (都合により関数内で設定を行う)
# servo = GPIO.PWM(pin_servo, 50)
# 回転角を定義
left   = 2.5
center = 7.25
right  = 12.0


##### プッシュボタンのセッティング #####
pin_open     = 10 
pin_close    = 27
pin_autolock = 9

GPIO.setup(pin_open,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_close,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(pin_autolock,GPIO.IN,pull_up_down=GPIO.PUD_UP)


#### グローバル変数(スプレッドシート記入用)
Terminal = 'button'



def press_open_button(pin_number):
    global Terminal
    counter = 0
    #print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_open)
        if status == 0:
            counter = counter + 1
            if counter >= 10:
                Terminal = 'button'
                print("開錠します")
                open()
                break
        else:
            #print("開錠を取り消します")
            break
        time.sleep(0.001)
    #print(counter)

def press_close_button(pin_number):
    global Terminal
    counter = 0
    #print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_close)
        if status == 0:
            counter = counter + 1
            if counter >= 10:
                Terminal = 'button'
                print("閉錠します")
                close()
                break
        else:
            #print("閉錠を取り消します")
            break
        time.sleep(0.001)
    #print(counter)

def press_autolock_button(pin_number):
    global Terminal
    counter = 0
    #print("GPIO[%d]のコールバックが発生しました" % pin_number)
    while True:
        status = GPIO.input(pin_autolock)
        if status == 0:
            counter = counter + 1
            if counter >= 10:
                Terminal = 'button'
                print("オートロックします")
                autolock()
                break
        else:
            #print("オートロックを取り消します")
            break
        time.sleep(0.001)
    #\print(counter)    




def open():
    global Terminal
    now = datetime.datetime.today()
    now_date = '{0:%Y-%m-%d}'.format(now)
    now_time = '{0:%H:%M:%S}'.format(now)
    lock_status ='開'
    
    servo = GPIO.PWM(pin_servo, 50)
    servo.start(center)
    time.sleep(0.3)
    servo.ChangeDutyCycle(left)
    time.sleep(0.3)
    servo.ChangeDutyCycle(center)
    time.sleep(0.3)
    servo.stop()

    cmd = ('python3 gsp-homeauto.py 0 {0} {1} {2} {3}'.format(now_date, now_time, Terminal, lock_status))
    subprocess.Popen(cmd.split())



def close():
    global Terminal
    now = datetime.datetime.today()
    now_date = '{0:%Y-%m-%d}'.format(now)
    now_time = '{0:%H:%M:%S}'.format(now)
    lock_status ='閉'
    
    servo = GPIO.PWM(pin_servo, 50)
    servo.start(center)
    time.sleep(0.3)
    servo.ChangeDutyCycle(right)
    time.sleep(0.3)
    servo.ChangeDutyCycle(center)
    time.sleep(0.3)
    servo.stop()

    cmd = ('python3 gsp-homeauto.py 0 {0} {1} {2} {3}'.format(now_date, now_time, Terminal, lock_status))
    subprocess.Popen(cmd.split())



def autolock():
    global Terminal
    now = datetime.datetime.today()
    now_date = '{0:%Y-%m-%d}'.format(now)
    now_time = '{0:%H:%M:%S}'.format(now)
    lock_status ='オートロック'
    cmd = ('python3 gsp-homeauto.py 0 {0} {1} {2} {3}'.format(now_date, now_time, Terminal, lock_status))
    subprocess.Popen(cmd.split())

    open()
    time.sleep(7)
    close()




def on_connect(client, userdata, flags, respons_code):
    print('status {0}'.format(respons_code))


def on_disconnect(client, userdata, flags, respons_code):
    if  respons_code != 0:
        print("Unexpected disconnection.")
    else:
        print('disconect')
        print(respons_code)
    client.loop_stop()
    print('11')


def on_message(client, userdata, msg):

    print(msg.topic + ' ' + str(msg.payload))
    data = json.loads(msg.payload.decode("utf-8"))["data"][0]

    global Terminal
    Terminal = data["terminal"]

    # control
    if (data["device"] == 'lock'):
        if   (data["action"] == 'open'):
            print('鍵を開けます')
            open()
            print('鍵を開けました')
        elif (data["action"] == 'close'):
            print('鍵を閉めます')
            close()
            print('鍵を閉めました')
        else:
            print('unknown message')

    else:
            print('unknown message')

def IC_read():
    global Terminal
    while True:
        felica = libpafe.felica_polling(pasori, FELICA_POLLING_ANY, 0, 0)
        idm = c_ulonglong() 
        libpafe.felica_get_idm.restype = c_void_p
        libpafe.felica_get_idm(felica, byref(idm))
        idm_No = "%016X" % idm.value
        if idm_No in allow_idm_list:
            now = datetime.datetime.today()
            now_date = '{0:%Y-%m-%d}'.format(now)
            now_time = '{0:%H:%M:%S}'.format(now)
            print('Allow: {0}'.format(idm_No))
            Terminal = 'IC_card'
            open()
            #cmd = ('python3 gsp-homeauto.py 2 {0} {1} {2} {3} {4}'.format(now_date, now_time, Terminal, idm_No, "Allow"))
            #subprocess.Popen(cmd.split())
        elif idm_No == '0000000000000000':
            #print('カードをタッチしてください')
            pass
        else:
            now = datetime.datetime.today()
            now_date = '{0:%Y-%m-%d}'.format(now)
            now_time = '{0:%H:%M:%S}'.format(now)
            print('deny: {0}'.format(idm_No))
            Terminal = 'IC_card'
            close()
            #cmd = ('python3 gsp-homeauto.py 2 {0} {1} {2} {3} {4}'.format(now_date, now_time, Terminal, idm_No, "Deny"))
            #subprocess.Popen(cmd.split())
        sleep(1)


if __name__ == '__main__':

    try:
        client = mqtt.Client()
        client.on_connect = on_connect
        client.on_disconnect = on_disconnect
        client.on_message = on_message
        client.username_pw_set('token:%s' % TOKEN)
        client.tls_set(CA_CERTS)
        client.connect(HOST, PORT)
        client.subscribe(TOPIC)

        libpafe = cdll.LoadLibrary("/usr/local/lib/libpafe.so")
        libpafe.pasori_open.restype = c_void_p
        pasori = libpafe.pasori_open()
        libpafe.pasori_init(pasori)
        libpafe.felica_polling.restype = c_void_p
        executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
        executor.submit(IC_read)

        GPIO.add_event_detect(pin_open, GPIO.FALLING, callback = press_open_button, bouncetime = 1000)
        GPIO.add_event_detect(pin_close, GPIO.FALLING, callback = press_close_button, bouncetime = 1000)
        GPIO.add_event_detect(pin_autolock, GPIO.FALLING, callback = press_autolock_button, bouncetime = 1000)
        
        client.loop_forever()
        

    except KeyboardInterrupt:
        print('finished')
        GPIO.cleanup()

このプログラムを使用するには3か所変更する必要があります。

1か所目と2か所目は “beebotteのセッティング” のところです。
プログラムの最初の方にある “beebotteのセッティング” の

TOKEN = 'token_xxxxxxxxxxx'
TOPIC = 'xxxxxxxx/xxxxxxxx'

を自分の beebotte の値に変更してください。(詳しくはこちらの記事のbeebotteの設定のところを読んでください。)

3か所目は “libpafe設定” のところです。
ここの allow_idm_list のところに使用するICカードの16桁のidm番号(16進数)を入力してください。

allow_idm_list = ['1234567890123456', '112233445566aabbb']

といってもidm番号を知ってる人はいないと思います。このプログラムを実行することでidm番号を確認することができます。

上記の1,2か所目を変更したらプログラムを実行し、ICカードリーダーにICカードをかざしてください。すると、

Deny: 13a57b9c24d68e09

と表示されると思います。この16桁の番号がidm番号なので、この値をallow_idm_listに入力してください。

  

③スプレッドシートへのICカードの記載

また、上記のプログラムではコメントアウトしていますが、231,232 241,242行目に記載したようなプログラムを実行することで、

上記の画像のようにタッチされたICカード情報を自動記録することができ、部外者によりタッチされたことも分かるようになります。

スプレッドシートへの記載方法については以下の記事を参照してください。

     

最後に

登録されているICカード( allow_idm_list に記載したICカード)がタッチされると

Allow: 13a57b9c24d68e09

と表示され、鍵が開きます。

一方、登録されていないICカードがタッチされると

Deny: 13a57b9c24d68e09

と表示され、鍵が閉まります。

これにより、ICカードが複数あれば、ICカードを鍵を開ける用と閉める用に振り分けることができます。

    

続きはこちら↓

google ads




google ads




-電子工作, ラズベリーパイ

執筆者:


  1. abiko より:

    スマートロックをラズベリーパイ4で作っています。ICカードでドアを開閉するプログラムを実行するとこのような(以下)エラーが出て解決策を探しています。教えてもらえるとありがたいです。

    >>> %Run pasori_idm2.py
    pasori_idm2.py:36: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
    GPIO.setup(pin_servo, GPIO.OUT)
    Traceback (most recent call last):
    File “/home/pi/pasori/libpafe/tests/pasori_idm2.py”, line 256, in
    client.tls_set(CA_CERTS)
    File “/usr/local/lib/python3.7/dist-packages/paho/mqtt/client.py”, line 831, in tls_set
    context.load_verify_locations(ca_certs)
    FileNotFoundError: [Errno 2] No such file or directory

    *プログラムの方は変えるところ以外は何も変更してはいません*
    よろしくおねがいします。

    • joruji より:

      コメントありがとうございます。
      一番目のGPIOに関するエラーは、二番目のエラーのせいで
      GPIO.cleanup()
      が実行されずにプログラムが終了してしまったために表示されていると思われるので、
      このエラーは無視しても大丈夫です。

      二番目のエラーについてですが、beebotteの証明書である、
      mqtt.beebotte.com.pem
      というファイルがこのプログラムファイル(pasori_idm2.py)と同じフォルダーにないため発生していると思います。
      こちら(https://jorublog.site/electronic-work-smart-lock/)を参考にして、
      プログラムファイルと同じフォルダーに証明書を置いてからプログラムを実行してみてください!

abiko へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


関連記事

ラズパイに外付けHDD(SSD)を接続する方法を徹底解説 – フォーマット、パーティション作成、マウント方法など

今回はラズパイでNAS(ファイルサーバー)を構築するために外付けHDDを購入したので、ラズパイで外付けHDDが使用できるようにする方法について解説します。(外付けSSDの接続方法も外付けHDDと同じで …

ラズパイでスマートロック作ってみた② - 扉の開閉状況をGoogleスプレッドシートに記録

以前作成したスマートロックに、扉の開閉状況をGoogleスプレッドシートに記録する、という機能をつけたので、そのやり方について紹介します。   搭載した機能 スイッチで鍵の開閉外部サービス(Googl …

Google Apps Scriptを使用してスプレッドシート上の図形を押した際に、プログラムを実行させる

    大学生の電子工作 ラズパイでスマートロック作ってみた⑤の記事に関連して、スプレッドシート上に設置したボタンを押した際に Google Apps Scriptを用いたプログラムを実行することで、 …

AmazonDushボタンを改造してできること- ラズパイやIoT製品との連携方法

AmazonDushボタンはひと手間加えるだけで、IoT製品等の遠隔ボタンとして使用することができます。これにより、スマートロックと連携させダッシュボタンで鍵を開閉したり、スマートリモコンのボタンとし …

大学生の電子工作 スマートリモコン(プログラム)

この記事ではスマートリモコンを作ってみたで紹介したスマートリモコンのプログラムを記載していきます。回路はスマートリモコン(回路)に記載してあります。今回作成するプログラムは以下の4つです。 (LED点 …




関連記事