🗂

Raspberry PiとNFCでスマートフォンでタッチするとリクエストを送るようにする

2023/10/04に公開

IMG_3137.JPG

必要なもの

Raspberry Pi 2 model B v1.2
image.png
PaSoRi RC-S380
image.png
TP-Link TL-WN725N
(Raspberry Pi 2 model BにWifiが搭載されていないため)
image.png
電磁スピーカー
PCのビープ音を出すスピーカーを使いました
image.png
電磁スピーカー Amazon URL

Raspberry Pi にOSをインストールする

下記サイトから Raspberry Pi Imager をインストール
Raspberry Pi OS

Raspberry Pi Imagerを起動したら、「OSを選ぶ」から「Raspberry Pi OS」を選択
image.png
「ストレージ」からはRaspberry Piに読み込ませるmicro sdカードを選択する
image.png
「歯車」マークから詳細な設定を行う
image.png
設定は下記のように設定

SSHを有効化する
    有効化
ユーザー名とパスワードを設定する
    ユーザー名: user
    パスワード: password
Wi-Fiを設定する
    SSID: 接続先のSSIDを設定 (※ Wi-Fiドングルが2.4GHz帯のWi-Fiしか認識できないので注意)
    パスワード: 接続先のパスワード
    Wifiを使う国: JP
ロケール設定する
    タイムゾーン: Asia/Tokyo
    キーボードレイアウト: jp

その他項目はデフォルトのまま「保存」を押下
image.png
設定が完了したら、「書き込む」を押下
image.png

TP-Link TL-WN725 のドライバインストール

新しめのWi-Fiドングルの場合、この作業は必要なさそうです。
Raspberry PiにディスプレイとLANケーブルとキーボードを接続して作業します。

パッケージを最新状態に更新

sudo apt update

インストール

sudo apt -y upgrade

カーネルヘッダをインストール

sudo apt install raspberrypi-kernel-headers git

ドライバをコンパイルしてインストール

git clone https://github.com/lwfinger/rtl8188eu.git
cd rtl8188eu/
make all
sudo make install

/etc/wpa_supplicant/wpa_supplicant.conf に設定を記載

sudo vi /etc/network/interfaces.d/wlan0

# 下記を追加
allow-hotplug wlan0
iface wlan0 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

rasberry-pi の再起動

sudo reboot

これで、Wi-Fiドングルが認識されるようになります。

参考:https://heavymoon.org/2022/02/05/raspberrypi-tp-link-tl-wn725n/

SSH で Raspberry Pi に接続

ping で Raspberry Pi のIPを調べる

ping raspberrypi.local

image.png

SSHで接続

ssh user@192.168.0.188
# OSを書き込む際に設定したユーザー名を設定
# ssh ${ユーザー名}@${ラズパイのIP}

参考:https://sukiburo.jp/setup-raspberry-pi/

NFCリーダーとの接続

NFCが認識されているか確認

lsusb

Sony Corp. RC-380 が認識されていればOK
image.png
nfcpy のインストール

sudo pip3 install nfcpy

サンプルを実行してNFCが機能するか確認
nfcpyのサンプルをgit cloneする

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

実行

sudo python3 nfcpy/examples/tagtool.py show

スマホやNFCカードが読み込まれると下記のようなログが出ます
image.png

sudo 無しで実行できるように設定

sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
sudo udevadm control --reload-rules
sudo reboot

index.py でアプリを作成

vi index.py

下記をコピペ
※ 交通系のICカード、iPhone、Android のみ反応します。
※ iPhone の場合、エクスプレスカードに設定した交通系のICが読み取られます

import binascii
import nfc
import requests

class MyCardReader(object):
    # iPhoneのエクスプレスカードにアクセスするための処理
    def on_startup(self, targets):
        for target in targets:
            target.sensf_req=bytearray.fromhex("0000030000")
        return targets

    def on_connect(self, tag):
        #タッチ時の処理
        print("--- Touched")

        #タグ情報を全て表示
        print(tag)

        #IDmのみ取得して表示
        self.idm = binascii.hexlify(tag._nfcid)
        print("IDm: {idm}", str(self.idm))

        return True

    def read_id(self):
        clf = nfc.ContactlessFrontend('usb')
        try:
            # 交通系IDまたはモバイルSuicaのみに限定
            clf.connect(rdwr={'targets':['212F'], 'on-startup': self.on_startup, 'on-connect': self.on_connect})
        finally:
            clf.close()


if __name__ == '__main__':
    cr = MyCardReader()
    while True:
        #最初に表示
        print("--- Please Touch")

        #タッチ待ち
        cr.read_id()

        #リリース時の処理
        print("--- Released")

実行

python3 index.py

IDmがログに出力されていればOK
image.png

参考:https://monomonotech.jp/kurage/raspberrypi/nfc.html
   https://qiita.com/xshell/items/55302a588b5927dde6b6

Google Apps Script(GAS)との連携

リクエスト元を作成するために、スプレッドシートにて「拡張機能」→「Apps Script」を選択
image.png
「デプロイ」→「新しいデプロイ」を選択
image.png
「種類の選択」→「ウェブアプリ」を選択
image.png
下記項目を入力し「デプロイ」を押下

ウェブアプリ	
	自分
アクセスできるユーザー	
	全員

image.png
デプロイが完了すると URL が発行されますので、メモって「完了」を押下
image.png
ソースコードを下記のように記述
リクエストが成功すると、status: '200' を返却します。
※ getSheetByNameのシート名を適宜書き換えてください

function doPost(e) {
  const idm = e.parameter.idm
  Logger.log('IDm: %s', idm)
  // シートオブジェ
  const sh = SpreadsheetApp.getActive().getSheetByName('シート名')
  sh.getRange(`A1`).setValue(idm)
  // レスポンス
  const output = ContentService.createTextOutput()
  output.setContent(JSON.stringify({status: '200'}))
  return output
}

「デプロイ」→「デプロイを管理」を押下
image.png
「鉛筆マーク」を押下し、バージョンを「新バージョン」を選択し「デプロイ」を押下
※ この方法じゃないとURLが変わってしまうため
image.png
NFCアプリの index.py を下記に書き換える

import binascii
import nfc
import requests

class MyCardReader(object):
    # スプレッドシートにリクエストを送る
    def request_gas(self, idm):
        url = 'https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # 先程生成したGASのURLを設定
        res = requests.post(url, data={'idm': idm}).json()
        print('http status: {status}'.format(status=res['status']))

    # iPhoneのエクスプレスカードにアクセスするための処理
    def on_startup(self, targets):
        for target in targets:
            target.sensf_req=bytearray.fromhex("0000030000")
        return targets

    def on_connect(self, tag):
        #タッチ時の処理
        print("--- Touched")

        #タグ情報を全て表示
        print(tag)

        #IDmのみ取得して表示
        self.idm = binascii.hexlify(tag._nfcid)
        print("IDm: {idm}", str(self.idm))

        # リクエスト
        self.request_gas(str(self.idm))

        return True


    def read_id(self):
        clf = nfc.ContactlessFrontend('usb')
        try:
            # 交通系IDまたはモバイルSuicaのみに限定
            clf.connect(rdwr={'targets':['212F'], 'on-startup': self.on_startup, 'on-connect': self.on_connect})
        finally:
            clf.close()

if __name__ == '__main__':
    cr = MyCardReader()
    while True:
        #最初に表示
        print("--- Please Touch")

        #タッチ待ち
        cr.read_id()

        #リリース時の処理
        print("--- Released")

index.py を実行して、スマートフォンをかざす
ログに「http status: 200」が出力されていることを確認
image.png
スプレッドシートにIdmが書き込まれていることを確認
image.png

ブザー音を鳴らす

ブザーの端子の位置を替える。
端子がツメに引っかかっているだけなので、マイナスドライバーなどでツメを持ち上げてあげれば簡単に抜けます。
image.png
スピーカーのプラス端子を12番目にマイナス端子を14番目に接続する
image.png
GPIO 画像:https://www.raspberrypi.com/documentation//computers/os.html#gpio-pinout

NFC読み取りの際に作成した index.py を修正

import binascii
import nfc
import requests
import buzzer

bz = buzzer.Buzzer()

class MyCardReader(object):
    # スプレッドシートにリクエストを送る
    def request_gas(self, idm):
        url = 'https://script.google.com/macros/s/AKfycbxV6byms_z8wFbfzF_Od6Pr2hVBsjXUm_j9PcrW9daH_GqdvgBcaHlvzar2lZu9zFgutg/exec' # 先程生成したGASのURLを設定
        res = requests.post(url, data={'idm': idm}).json()
        print('http status: {status}'.format(status=res['status']))

    # iPhoneのエクスプレスカードにアクセスするための処理
    def on_startup(self, targets):
        for target in targets:
            target.sensf_req=bytearray.fromhex("0000030000")
        return targets

    def on_connect(self, tag):
        #タッチ時の処理
        print("--- Touched")
        bz.touch()

        #タグ情報を全て表示
        print(tag)

        #IDmのみ取得して表示
        self.idm = binascii.hexlify(tag._nfcid)
        print("IDm: {idm}", str(self.idm))

        # リクエスト
        self.request_gas(str(self.idm))

        return True


    def read_id(self):
        clf = nfc.ContactlessFrontend('usb')
        try:
            # 交通系IDまたはモバイルSuicaのみに限定
            clf.connect(rdwr={'targets':['212F'], 'on-startup': self.on_startup, 'on-connect': self.on_connect})
        finally:
            clf.close()


if __name__ == '__main__':
    cr = MyCardReader()
    while True:
        #最初に表示
        print("--- Please Touch")

        #タッチ待ち
        cr.read_id()

        #リリース時の処理
        print("--- Released")

ブザーを鳴らすために下記のクラスを buzzer.py で作成

vi buzzer.py

下記をコピペ

import pigpio
from gpiozero import TonalBuzzer
from gpiozero.pins.pigpio import PiGPIOFactory
from time import sleep

# BUZZERのピン
BUZZER_PIN = 18

# TONEレベル
TONE_LV1 = "C4"
TONE_LV2 = "D4"
TONE_LV3 = "G4"
TONE_LV4 = "B4"
TONE_LV5 = "E5"

class Buzzer(object):
    def __init__(self):
        factory = PiGPIOFactory()
        self.bz = TonalBuzzer(BUZZER_PIN, pin_factory=factory)

    # ブザー音を鳴らす
    def ring(self, note, time):
        self.bz.play(note)
        sleep(float(time))
        self.bz.stop()

    # タッチ時
    def touch(self):
        self.ring(TONE_LV5, '0.2')

GPIOを制御するために、pigpioを有効化します

sudo pigpiod

動作確認

python3 index.py

タッチするとブザー音がなるようになっています。

Raspberry Pi 起動時に自動的にアプリを立ち上げる

NFCアプリをサービス化させる

sudo vi /usr/lib/systemd/system/nfc-touch.service

下記内容を記載
ExecStartに NFCアプリのパスを設定

[Unit]
Description=NFC Touch

[Service]
ExecStart=/usr/bin/python3 /home/user/index.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

サービスの起動

sudo systemctl daemon-reload
sudo systemctl start nfc-touch.service

サービスの起動確認

sudo systemctl status nfc-touch.service

Active: の項目が active(running) となっていることを確認
image.png
サービスの有効化

sudo systemctl enable nfc-touch.service

pigpioを起動時から有効にするためサービスを有効化

sudo systemctl enable pigpiod.service

Raspberry Pi を再起動

sudo reboot

これで電源を入れる際にNFCタッチアプリが起動するようになりました。

Discussion