ジムの会員管理システムを作った僕に「エニタイムフィットネスみたいなことがしたい」とジムを家族経営するお客さんから相談された。
「えっ!?会員管理を作ったついでにエニタイムフィットネスみたいな仕組みをやりたい!?予算は無い!?不正防止のため、入退室時の写真も撮りたい?!ログもとりたい!?」
さすが筋トレに明け暮れてるオーナーさんの要望はマッチョだと思った。
普通にやれば電子錠の仕組みや工事やらで一店舗あたり数百万から一千万掛かるような仕組みだろう。
そんな予算無いみたいだし、既存の店舗をそんな大々的に工事もできない。そもそも自分にそんな工事の知識もない。
結果Raspberrypiを使い、それを一店舗予算10万円代で実現、会員カードを他店舗と共有した24時間営業にできた。
その詳しい技術的な内訳を共有する。
(なお執筆時点では2024年だが、これ自体は5年前、2019年の仕事である。)

## 鍵開錠の様子(開場後自動で閉じる)
https://youtube.com/shorts/cHkDXyWnqGQ?feature=share
https://youtube.com/shorts/L38OvMiYM4k?feature=share
5年前は民泊などの影響もあり、ちょうど鍵のIOT機器やサービスが出始めたころであった。
そこで別のAkerunなどの開錠システムも検討したが、
こちらで制作したジムの会員管理とシームレスに連携できない
電池式で不安
(電池が切れた場合夜中に入れなくなる可能性がある。)
要件として退出時にもカメラを撮影したいとリクエストされていた
月額サブスクをクライアントが嫌がった
どうしてもネットワークを介して解錠命令を送るためカードをかざしたあとのラグが気になった
等の点により自前で実装することにした。
結果、クライアントがネットで探してきた「NOAKEL」という端末を調べた所、これが使えそうだと思った。
そこでRaspberryPiとICカードリーダーを使い、
またテストしたところ、ガラス戸レベルであれば外側から触れても接触にあまり問題なかったため、扉の内外両方からICカードをかざすことが可能であった。
図示するとこんな感じ

図示するとこんな感じ

新規会員登録時にICカードのIDを紐づけてもらう作業を行い、それを会員に対して発行する。
会員が入室時にICカードリーダーをかざした際、読みとったIDを会員管理システムにAPIで確認し、OKな会員かNGな会員か(例えば滞納してるなど)をチェックする。
RaspberryPi側では、GPIOを操作し、タッチ音としてリレー出力で圧電ブザーを鳴らすと共に、cv2を使いRaspberryPiにUSBでつないだカメラでキャプチャを行い、それをRaspberryPiのシリアル番号と共にアプリケーション側に送る。
そして開錠OKな会員であれば、GPIOの接点出力モジュールからNOAKELに対して開錠命令を送る。
#!/usr/bin python# -*- coding: utf-8 -*-import osimport requestsimport jsonimport binasciiimport nfcimport threadingimport timeimport cv2import base64import mimetypesimport RPi.GPIOas GPIOfrom classesimport sound#My Definition Classfrom classesimport relay#My Definition Class#from classes import camera #My Definition Classfrom libimport functions#My Definition Functions# Suica待ち受けの1サイクル秒TIME_cycle=1.0# Suica待ち受けの反応インターバル秒TIME_interval=0.2# タッチされてから次の待ち受けを開始するまで無効化する秒TIME_wait=3GPIO_PORT=4#realyIMG_FILE='/home/pi/amegym/image/picture.jpg'API_URL='[APIのURL]'ACCESS_TOKEN='[APIのアクセストークン]'# NFC接続リクエストのための準備target_req_felica= nfc.clf.RemoteTarget("212F")# 106A(NFC type A)で設定target_req_nfc= nfc.clf.RemoteTarget("106A")cam=Nonerasp_id= functions.getserial()#シリアルナンバーの取得print('Waiting for Tag...')whileTrue:# USBに接続されたNFCリーダに接続してインスタンス化 clf= nfc.ContactlessFrontend('usb')# clf.sense( [リモートターゲット], [検索回数], [検索の間隔] ) target_res= clf.sense(target_req_nfc,target_req_felica, iterations=int(TIME_cycle//TIME_interval)+1, interval=TIME_interval)# カメラデバイスを取得if camisNone: cam= cv2.VideoCapture(0)# カメラFPSを60FPSに設定 cam.set(cv2.CAP_PROP_FPS,30) cam.set(cv2.CAP_PROP_FRAME_WIDTH,1920) cam.set(cv2.CAP_PROP_FRAME_HEIGHT,1080)ifnot target_resisNone: beep= sound.SOUND() beep.onRead() tag= nfc.tag.activate(clf, target_res) tag.sys=3#IDmを取り出す idm= binascii.hexlify(tag._nfcid).upper()# readで画像をキャプチャ、imgにRGBのデータが入ってくる res, img= cam.read()# 保存 cv2.imwrite(IMG_FILE, img)print('took picture!') prepend_info='data:%s;base64'% mimetypes.guess_type(IMG_FILE)[0] base_64_data= base64.b64encode(open(IMG_FILE,'rb').read()).decode("ascii") image_data_base64='%s,%s'%(prepend_info, base_64_data)print('data encoded') request_args={"RFID": idm,"DeviceID": rasp_id,"access_token": ACCESS_TOKEN,"image": image_data_base64,}#session = requests.session() serverRes= requests.post(API_URL, request_args)print(serverRes)if serverRes.status_code==200:print(serverRes.text.encode("utf-8")) res= serverRes.json()if res["msg"]=="success":print("relay on") GPIO.setmode(GPIO.BCM) GPIO.setup(GPIO_PORT, GPIO.OUT, initial=1) GPIO.output(GPIO_PORT,1) time.sleep(0.5) GPIO.output(GPIO_PORT,0) GPIO.cleanup()else: beep.onFalse()#end ifelse: beep.onError()print(res) cam.release() cam=Nonedel beepprint('sleep '+str(TIME_wait)+' seconds') time.sleep(TIME_wait) clf.close()#end whileRaspberryPiも半導体の影響で値上がりしたが、材料費でNOAKELを入れても数万円でから十数万円程度で店舗を24時間営業にすることが出来た。
これを現在6店舗で展開しているが、大きなトラブルも無く5年間運用している。
バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。
株式会社en-gine代表。AIDXで企業の悩みを解決します。AIチャット、AI OCR、AI 営業ツールTypeScript、Next.js、Go、Flutter、AWS、Google Cloud各種