怠惰になるために本気出す

電子工作で色々作ってます

Sesame解錠編② pasoriと接続

今回はpasoriを接続してICカードidmと呼ばれるカード毎に固有の番号が付与されているので、pasoriでその番号を読み出して鍵を開けるためのしきい値として用います。

pasoriの接続確認

まずはpasoriがラズパイで認識しているか確認していきます。
pasoriをラズパイのUSBに挿し、ターミナルを起動後に
f:id:trkn5296:20220222171616p:plain

lsusbを入力してEnter。そうするとRC-S380と表示されているので認識されていることがわかります。
f:id:trkn5296:20220223121921j:plain

nfcpyのインストール

nfcpyというライブラリが公開されているのでインストールします。ターミナルを開いて下記のコードを入力するとインストールしてくれます。

 sudo pip3 install nfcpy


終わったらGithubで公開されいるサンプルコードを利用して動作確認していきます。
同じくターミナルで

git clone https://github.com/nfcpy/nfcpy.git

ダウンロードが終わったら下記をコピペ

cd nfcpy
sudo python3 examples/tagtool.py show

リーダーが待機状態となるので交通系ICカードをかざすと色々表示されますが、赤枠部分がIDがIDmです。
f:id:trkn5296:20220223172333p:plain


この状態だとsudoをつけないとプログラムが実行した際にラズパイがpasoriを認識することができずエラーとなって止まってしまいます。
ターミナルを開いて下記を入力

sudo nano /etc/udev/rules.d/nfcdev.rules

以下の1行をコピペして「ctrl+o」→「Enter」→「ctrl+x」

SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="06c3", GROUP="plugdev" # Sony RC-S380/P

書き込みが終わったら再起動します。これでsudoなしでも実行できるようになります。

追記(22/02/22)
以前同じことをやったときは特に問題なくsudoなしで実行できていたのですが、改めてやったら何故かうまくいきませんでした。
最悪ターミナル上からsudo有でプログラムを実行してやれば動くので特段支障はありません。


pythonで指定したICカードのみ動作させる

pythonでプログラムの動作確認を行っていきます。

import binascii, nfc, time
from threading import Thread, Timer
            
idm1 = b"xxxxxxxxxxxxxxxx" #交通系ICカード1
idm2 = b"yyyyyyyyyyyyyyy" #交通系ICカード2

#ICカード待受の1サイクル秒
TIME_cycle = 1.0
#ICカード待受の反応インターバル秒
TIME_interval = 0.2
#タッチされてから次を開始するまでの無効化する秒
TIME_wait = 3

#NFC接続リクエストのための準備
#212F(Felica)で設定
target_req_ic = nfc.clf.RemoteTarget("212F")
#0003(IC cart)
target_req_ic.sensf_req = bytearray.fromhex("0000030000")

print('ICカードをタッチしてください...')

while True:
    
    #USB接続されたカードリーダーをインスタンス化
    clf = nfc.ContactlessFrontend('usb')
    #ICカード待受開始
    target_res = clf.sense(target_req_ic, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)
    
    if not target_res is None:
        tag = nfc.tag.activate_tt3(clf, target_res)
        tag.sys = 3
        
        #IDmを取り出す
        idm = binascii.hexlify(tag.idm)
        print(idm)
        
        #特定のIDmだった場合のアクション
        if idm == idm1 or idm == idm2:
            print("OK")
            
        else:
            print("NG")
            
    clf.close()
#end while

今回はIf文と呼ばれる条件式を用いて、以下の青枠が条件式でそれに対して正だった場合に緑枠の動作(OKと表示)、そうでなかった場合に赤枠の動作(NGと表示)が行われるようになっています。プログラム上の「idm」はタッチされたICカードidmなので、「idm1」と「idm2」は鍵として使いたいidm入れてやると指定したICカードのみOKと表示される仕組みが完成します。
f:id:trkn5296:20220223171527j:plain

今後は緑色の動作内容を変更してICカードが一致した場合にSesameを解錠する動作内容に変更していきます。


次回:いよいよSesame登場

Sesame解錠編① Raspberry piのセットアップ及びVNC接続

今回はRaspberry Pi(以下ラズパイ)を使えるようにするために設定していきます。

 

電子機器全般に言えることですが購入してすぐに使えるわけではなく、セットアップを行わなければいけません。iPhoneだと言語設定やFace ID、Apple IDにログイン、ソフトのアップデートなどの設定があり、電源を入れると自動的に表示してやってくれますが、ラズパイは基本的にユーザーである私たちが主導になってやらないと何もやってくれません。

本記事ではmicro SDのOSのセットアップから始めますが、スイッチサイエンスではRaspberrry Piスターターキットが販売されており、microSDをセットアップ済みのものを同梱されているので初めて電子工作がという方はそちらを利用することをお勧めします。

www.switch-science.com

 

必要なもの

Raspberry Pi(以下ラズパイと略します)

・micro SD

HDMIケーブル

電源ケーブル

・モニタ

・キーボードとマウス(Bluetooth接続以外のもの)

 

microSDセットアップ&起動&VNC接続

こちらを参考にしてセットアップしました。

www.youtube.com

 

非常にわかりやすく説明されているので参考になりました。こちらの動画ではRaspberry pi zero 2のセットアップを行っていますが、Raspberry Pi 4だろうがRaspberry Pi zero WHだろうがmicroSDのセットアップ方法及び起動方法など基本的なところは変わりません。また、VNC接続(chromeのリモート接続のようなもの)のセットアップも行っておくとwindowsなどのパソコンでリモートで操作できるので楽です。

Raspberry Pi Zero 2は日本では技適を通過していないため日本では残念ながらまだ正式に販売はされていません。

 

アップデート

セットアップしたときから時間がたっているため記憶が定かではありませんがこちらの記事を参考にしてセットアップした気がします。

qiita.com

 

お疲れさまでした。これでセットアップが完了です。

 

 

続編へ

iotblog5296.hatenablog.com

 

Sesame3+raspberry piでICカードで解錠(総集編)

f:id:trkn5296:20220222225751p:plain

家の鍵を開け閉めするとき以外は基本的にカバンにいれているのですが、カバンの奥底に入っていて探すのに苦労するうえに、両手がふさがっていようものなら一度荷物を置いて探さなけれならないので煩わしさ以外なにものでもありません。ならばICカードで施錠・解錠できるようになれば怠惰になれる!と嬉々としながら取り組んでいたような気がします。

最近Qrio lockがQrio padというスマートロックとは別売りの物を買えばICカードで開け閉めできるようになるのですがとくかく高く、本体と合わせると5万円近くします。それに対してSesameRaspberry Piと呼ばれるマイコンと組み合わせればICカードで解錠できるように公式が仕組みを公開しています。

jp.candyhouse.co

 

この記事はSesame mini用に公開されたものなので残念ながらSesame3や4には使えませんが、Sesame3/4用の仕組みも公開されているのでそちらを使えばICカードでの解錠ができるようになります。また、仕組みを理解していればICカードだけでなくパスワード解錠や指紋認証、顔認証などもできるようで実際にそういった使い方されている人もいるようです。

価格、汎用性ともに勝るSesameを使わないわけがないので今回はsesameを使ったICカードによる解錠方法を解説していきます。

 

概要

ICカードを読み取る

②登録したICカードと一致したときにcandy houseに鍵の状態をリクエス

③鍵の状態が返ってきます

④(状態に応じて)開けるor閉める指示

⑤正常に動作したかのレスポンス

⑥カードが違う場合やSesameがネットから切れている場合にブザーを鳴らす

⑦エラー時に内容をGmailで通知

 

f:id:trkn5296:20220222203159p:plain

 

必要なもの

・Sesame3(orSesame4)

Sesame Wifiモジュール

pasori(RC-S380)

・QIコネクタ メスピン&ハウジングコネクタ

 

完成までの道中

・セットアップ

iotblog5296.hatenablog.com

 

pasoriと接続&動作確認

iotblog5296.hatenablog.com

Sesameを解錠

iotblog5296.hatenablog.com

・エラー時にブザーを鳴らす

iotblog5296.hatenablog.com

raspberry piGmail送信

 

 

 

これらの工程を経て完成したスマートロックがこちらです。読みにくいですが「OK」と「NG」の2枚のICカードを用意し、OKの方のICカードは登録してある状態です。

youtu.be

 

運用してから1ヵ月ぐらい経過したのですが、かなり便利で気に入っています。

家のネット環境はモバイルWiFiを固定回線として利用しているのですが、結構切れていることがあるようでエラーでスクリプトが停止していることがちょくちょくあります。

WEB APIなので自宅のネット環境に左右されるため、家の鍵を持たずに外出すると締め出されるなんてこともそのうちありそうですね

公式がBluetooth APIをいずれ公開してくれるとかくれないとか噂はあるのでCandy houseの今後に期待したいところです。

 

 

2年前ぐらい前にESP32でスマートロックを自作したことがあったのですが、

・家の鍵が頑丈すぎてサーボモータのトルクが足りない(これはモータを変えれば済む)

・筐体の知識がなく、線がむき出しで見栄えが悪い

・フラットケーブルの接触が悪くプログラム起動時に反応しない時がある

最後の項目が結構致命的で、ギミックを作った段階でボツとなってしましました。お披露目する機会がないのでここで供養させてください。

 

www.youtube.com

 

Sesame解錠編③ Sesame3をWEB APIで解錠

今回はWEB APIを使ってSesameを解錠させます。
全体の概要はこちら
iotblog5296.hatenablog.com

UUIDの確認

セサミ公式アプリに下記の赤枠にUUIDが表示されます。

また、「このセサミの鍵をシェア」を押して表示されるQRコードを画像として保存しておきます。

APIキーの取得

公式サイトからこちらからAPIキーを取得します
https://partners.candyhouse.co/login

リンクに飛んでアドレスを入力し、指定したアドレスに記載してある数字を入力すると


黒塗りしている部分に個人のAPIキーが表示されるのでそれをメモします。


Sesameの鍵の状態(RESTer)

chrome拡張機能を使って動作確認してきます。
Method: GET
URL: https://app.candyhouse.co/api/sesame2/[操作するSESAMEのUUID]
Header:
 Name:x-api-key
 Value: [API_KEY]
上記の[ ]部分は先程メモした
UUIDとAPIキーを入力します(入力する際は[ ]は不要です)

正常に動作したので200が返ってきています。
取得した状態の内容は
batteryPercentage: 電池残量
batteryVoltage: 電池電圧
position: サムターン角度
CHSesame2Status: 状態
timestamp: 更新時刻

statusの箇所で"locked"と表示されており、実際に鍵も施錠しているので一致していました。

正常に動作しない場合、どこかしらのネットが切れている可能性が高いのでWifiモジュールがエラーを起こしていないか、そもそもネット自体切断されていないかなどの確認をしてみてください。


secret_keyの取得

WEB APIを取得したページを開き赤丸部分を押して保存しておいたQRコードの写真を選択します。


すると赤枠部分にsecret_keyが表示されるのでメモします。

sesameのweb apiを取得するページの表示方法が気が付いたら変わっていて、以前は自分でシークレットキーをhexに変換していましたがこちらのページ上で表示されるようになったため不要になりましたがこういう方法もあります

pythonSesameの状態取得

web apiでいけることがわかったので今度はpythonでコードを書いて試していきましょう
コードを書くにあたりライブラリをインストールします。
ターミナルを起動して下のコードを入力します。

$ pip install pycryptodome, requests


インストールが終わったら動かしてみましょう。Thonny python IDEを起動して


下のコードをコピペして各々のUUID、API、シークレットキーを入力してRunします。

import datetime, base64, requests, json
from threading import Thread, Timer
from Crypto.Hash import CMAC
from Crypto.Cipher import AES

uuid = '個人のUUID'
secret_key = '個人のシークレットキー'
api_key = '個人のAPIキー'

# HTTP header
headers = { 'x-api-key': api_key }

# signの生成
cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES)
message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4]
cmac.update(message)
sign = cmac.hexdigest()
            
#鍵の状態取得
surl = f'https://app.candyhouse.co/api/sesame2/{uuid}'
sres = requests.get(surl, headers=headers) #リクエスト
print(sres.text)


するとShell部分に先程chrome拡張機能で行った状態と同じものが表示されます。


pythonで解錠処理

状態取得は確認できたので解錠処理も確認していきます。先程と同様に下のコードをコピペしてRunします

import datetime, base64, requests, json
from Crypto.Hash import CMAC
from Crypto.Cipher import AES

uuid = '個人のUUID'
secret_key = '個人のシークレットキー'
api_key = '個人のAPIキー'

# 鍵の操作(toggle/lock/unlock)
cmd = 83    # 88/82/83 = toggle/lock/unlock


# HTTP header
headers = { 'x-api-key': api_key }

# 履歴に残す内容
history = 'open (Web API)' # 半角21文字/全角不明
history = base64.b64encode(history.encode()).decode()

# signの生成
cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES)
message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4]
cmac.update(message)
sign = cmac.hexdigest()

# 鍵の操作
url = f'https://app.candyhouse.co/api/sesame2/{uuid}/cmd'

body = {
    'cmd': cmd,
    'history': history,
    'sign': sign
}
res = requests.post(url, json.dumps(body), headers=headers)
print(res.status_code, res.text)


正常に動作するとshellに200が表示されます。


特定のカードをタッチしたときのみアクションを起こす

状態取得するコードと解錠するコード、以前書いたpasoriidmを抜き出すコードを組み込んでいきます。

import datetime, base64, requests, json, binascii, nfc, os, time
from threading import Thread, Timer
from Crypto.Hash import CMAC
from Crypto.Cipher import AES


idm1 = b"お手持ちのICカード1" 
idm2 = b"お手持ちのICカード2" 

#ICカード待受の1サイクル秒
TIME_cycle = 1.0
#ICカード待受の反応インターバル秒
TIME_interval = 0.2
#タッチされてから次を開始するまでの無効化する秒
TIME_wait = 3

#NFC接続リクエストのための準備
#212F(Felica)で設定
target_req_ic = nfc.clf.RemoteTarget("212F")
#0003(IC cart)
target_req_ic.sensf_req = bytearray.fromhex("0000030000")


print('ICカードをタッチしてください...')

while True:
    
    #USB接続されたカードリーダーをインスタンス化
    clf = nfc.ContactlessFrontend('usb')
    #ICカード待受開始
    target_res = clf.sense(target_req_ic, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)
    
    if not target_res is None:
        tag = nfc.tag.activate_tt3(clf, target_res)
        tag.sys = 3
        
        #IDmを取り出す
        idm = binascii.hexlify(tag.idm)
        print(idm)
        
        #特定のIDmだった場合のアクション
        if idm == idm1 or idm == idm2:
            uuid = '個人のUUID'
            secret_key = '個人のシークレットキー'
            api_key = '個人のAPIキー'

            # HTTP header
            headers = { 'x-api-key': api_key }

            # signの生成
            cmac = CMAC.new(bytes.fromhex(secret_key), ciphermod=AES)
            message = int(datetime.datetime.now().timestamp()).to_bytes(4, 'little', signed=False)[1:4]
            cmac.update(message)
            sign = cmac.hexdigest()
            
            print("【 特定のIDmにより施解錠 】")
            #鍵の状態取得
            surl = f'https://app.candyhouse.co/api/sesame2/{uuid}'
            sres = requests.get(surl, headers=headers) #リクエスト
            print(sres.text)
            
            if "unlocked" in sres.text:
                print("close")
                cmd = 82
                history = 'close (Web API)'

            else:
                print("open")
                cmd = 83
                history = 'open (Web API)'
            history = base64.b64encode(history.encode()).decode() #履歴に「open」or「close」を記載
            url = f'https://app.candyhouse.co/api/sesame2/{uuid}/cmd'
            body = {
                'cmd': cmd,
                'history': history,
                'sign': sign
            }
            res = requests.post(url, json.dumps(body), headers=headers)
            print(res.status_code, res.text)
            print('sleep'+ str(TIME_wait)+ ' second') 
            time.sleep(TIME_wait)
            
    clf.close()
#end while

正常だとshellにこんな感じで表示されます。



土台となるギミックはこれで完成です。
あとは必要に応じてGmailで通知を送ったりブザーを鳴らしたり機能を追加していきます。

次回:エラー時にブザーを鳴らす
iotblog5296.hatenablog.com