大学生の電子工作 ラズパイでスマートロック作ってみたの記事の④つ目の記事です。今回はSuicaや学生証、電子マネー(nanacoとか)等のICカードで鍵を開けられるようにしました。
近くのコンビニに買い物に行くときなどは、財布だけをもってスマホを持たずに外出してしまうことが何度かありました。(そのようなときは研究室のパソコンから自宅のラズパイにVNCで直接アクセスし鍵を開けました。)
現状私の家の玄関を開けるには、
- 鍵、合鍵で開ける
- スマホで開ける
- AmazonDushボタンで開ける
- (Raspberry piにアクセスして鍵を開けるコマンドを実行する。)
などの方法がありますが、財布のみをもって外出してしまった場合、鍵を開ける手段がありません。
スマホも財布も持たずに外出することは考えられないので、今回は財布に入っているICカードで開けられるようにし、スマートロック完全体を作成してスマートロックに締め出されないようにしようと思います!
また、スマホの手帳型ケースにもICカードが入っているので、スマホを操作せずともスマホをタッチするだけで鍵が開けられるようにもなります。
目次
搭載した機能
- スイッチで鍵の開閉
- 外部サービス(Google Assistant, Alexa)と連携して外部から鍵の開閉
- 開閉記録をgoogleスプレッドシートに記述
- AmazonDushボタンで鍵の開閉
- ICカードで鍵を開ける
- スプレッドシートをICカードのデータベースとして使用する
実行環境
- ラズベリーパイ3 B+
- ICカードリーダー ( RC-S320 )
Raspberry Pi4 ModelB 4GB ラズベリーパイ4 技適対応品
SONY RC-S320 非接触ICカードリーダ/ライタ PaSoRi 「パソリ」
手順
- ICカードを読み取るための基本設定
- ICカードで鍵を開けるプログラムの作成
- スプレッドシートへの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カードを鍵を開ける用と閉める用に振り分けることができます。
スマートロックをラズベリーパイ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
*プログラムの方は変えるところ以外は何も変更してはいません*
よろしくおねがいします。
コメントありがとうございます。
一番目のGPIOに関するエラーは、二番目のエラーのせいで
GPIO.cleanup()
が実行されずにプログラムが終了してしまったために表示されていると思われるので、
このエラーは無視しても大丈夫です。
二番目のエラーについてですが、beebotteの証明書である、
mqtt.beebotte.com.pem
というファイルがこのプログラムファイル(pasori_idm2.py)と同じフォルダーにないため発生していると思います。
こちら(https://jorublog.site/electronic-work-smart-lock/)を参考にして、
プログラムファイルと同じフォルダーに証明書を置いてからプログラムを実行してみてください!