📄

ラズパイとNFCで在庫管理システムを作ろう

commits8 min read

ラズベリーパイとNFCリーダを使って、在庫管理システムを作りました。
きっかけや作り方について紹介します。

https://twitter.com/tw_kotatu/status/1444460741621207040

個人の趣味レベルです

💡きっかけ

調味料の買い忘れや多めに買ってしまい賞味期限が切れたりと、何か仕組みをつくれないかな思いました。
また、NFCリーダを購入したこともあり、何か利用できないかと思っていました。

🤔やりたいこと

  • 買い物中に、切らしているのかどうかを把握したい
    • スマホで確認できること
  • 在庫の登録は簡単にしたい
    • 専用の登録機材を作る
    • 機材も持ち運びできること
  • NFCリーダを利用する
    • 目的と手段が混同しますが、とりあえず使ってみたい
    • 後々別のシステムで利用方法がありそうなので、勉強しておきたい

⚙全体像

使用する機材と利用するサービスは以下となります。

機材

  • NFCリーダ
  • NFCタグ
  • ラズベリーパイゼロWH
    • 持ち運びするため、モバイルバッテリーで駆動したいためゼロを使います
  • 電子工作部品
    • LED : 起動やエラーなどを通知するために使用
    • ブザー : NFCリーダ反応時の通知のために使用
    • 表示デバイス(OLED) : 登録情報の一時表示に使用
  • IoT機器対応モバイルバッテリー

サービス

  • Googleスプレッドシート
    • 在庫情報の登録および確認に使用します
  • IFTTT
    • Webhook -> Googleスプレッドシートへの登録に使用します
  • ネットワークプリント
    • タグの装飾をするのに、ローソンのシール印刷を使用しました

GCPの設定をすれば、直接スプレッドシートを制御できます。
その場合は、IFTTTは不要です。

システム図

在庫の登録

  1. 調味料の在庫有無を登録
    • 在庫がある場合は、在庫ありタグをNFCリーダで登録
    • 在庫がない場合は、在庫なしタグをNFCリーダで登録
  2. NFCのタグIDに応じて、IFTTTに対しwebhookを送信
    • Googleスプレッドシートを更新

出先での確認

  • 在庫表を確認
    • 足りないものを購入する

📌作ったもの

機材を組み合わせて、以下の構成になっています。

コード

本システムのコードは、↓に配置していあります。

https://github.com/kotaproj/pi_nfcrd

以下から、ポイントを記載します。

NFCリーダの制御

NFCリーダの基本的な使い方は、↓にまとめておきました。

https://zenn.dev/kotaproj/books/raspberrypi-tips/viewer/450_kiso_nfc

本システムでは、NFCリーダにて、NFCをリードすると下記の関数がよばれます。

nfcrd.py
    def on_rdwr_connect(self, tag):
        #IDmの表示
        self.idm = binascii.hexlify(tag._nfcid)
        print("IDm : " + str(self.idm))

        #特定のIDmだった場合
        for k, v in self._sysdat.json_dict.items():
            if self.idm == k.encode():
                print(" 登録されたID:", k, v)
                self._send_msg(k, "registered")
                return

        self._send_msg("unknown", "touched")

        return True

tagには、NFCタグIDがはいっており、特定IDの場合、キューを経由してイベント管理-presenter.pyへ通知します。
監視する特定IDは、下記のように管理しています。

settings.json
{
    "04586c6a266581":{ <=監視する特定ID
        "id":"soy_sauce_none",
        "name":"醤油",
        "event_id":"update_gs_inventory", <=IFTTTのイベントID
        "cell":"C5", <=スプレッドシートのセル番号
        "value":"なし", <=スプレッドシートに入力するデータ
        "note":""
    },
    "04102001af4903":{
        "id":"soy_sauce_many",
        "name":"醤油",
        "event_id":"update_gs_inventory",
        "cell":"C5",
        "value":"あり",
        "note":""
    },

使用するNFCタグは、すべて事前に登録しておきます。

通知デバイス(LED/ブザー/表示機)の制御

各通知デバイスの使い方は、下にまとめてあります。

  • LED

https://zenn.dev/kotaproj/books/raspberrypi-tips/viewer/010_kiso_lchika
  • ブザー

https://zenn.dev/kotaproj/books/raspberrypi-tips/viewer/030_kiso_buzzer
  • 表示機

https://zenn.dev/kotaproj/books/raspberrypi-tips/viewer/250_kiso_ssd1306

コード上で指定しているピンは以下となります。

ラズパイ 接続先 備考
2 OLED - SDA I2C1
3 OLED - SCL I2C1
18 ブザー PWM
16 LED - RED エラー通知用
20 LED - GREEN 起動確認用
21 LED - BLUE NFC判定用

詳細な回路図を確認したい方は、↓となります。

https://github.com/kotaproj/supportPiJig/wiki/020_サンプルプログラム

以前、別目的で作った基板です。
この基板を流用しています。

Googleスプレッドシートへの送信

事前準備(IFTTT)

IFTTT - 無料プランでは3つまでしか同時にActiveできません。
必要に応じて、課金(Proへの移行)やAppletを無効にするなどが必要です。

Appletの登録

IFTTTの設定手順について説明します。

  • https://ifttt.com/home へアクセスしてアカウントを作成 or ログイン
  • Createにて、以下を追加
    • This(Trigger)
      • "Webhooks" - "Receive a web request"
        • "Event Name" : update_gs_inventory
    • That(Action)
      • Update cell in spreadsheet
        • Drive folder path : IFTTT/MakerWebooks/home
        • Spreadsheet name : inventory_list
        • Which cell? : Value1
        • Value : Value2

Value1, Value2をIFTTT管理 - ifttt.pyより指定します。

tokenの確認

webhooksをキックするためにtokenを確認します。

  • Webhooks Settingsのページより確認する
  • 下記画像のマスクされている箇所に記載されている

IFTTTへの送信

IFTTTへの送信は、下記の関数が呼ばれます。

ifttt.py
    def _post_request(self, name):
        val = self._sysdat.json_dict[name]

        payload = {"value1": "",
                    "value2": "",
                    "value3": "" }
        payload["value1"] = val["cell"]
        payload["value2"] = val["value"]
        payload["value3"] = ""

        url = "https://maker.ifttt.com/trigger/" + val["event_id"] + "/with/key/" + IFTTT_TOKEN

        try:
            response = requests.post(url, data=payload)
            if 200 == response.status_code and "Congratulations" in response.text:
                self._message_error(ErIfttt.OK)
            else:
                self._message_error(ErIfttt.ER_POST)
            response.close()
        except requests.exceptions.RequestException as e:
            print("[ifttt_th] e", e)
            self._message_error(ErIfttt.ER_EXCEPTION)
        return

コードとIFTTTと関係は以下となります。

IFTTT コード 備考
"Event Name" val["event_id"] "update_gs_inventory"
token IFTTT_TOKEN "tokenの確認"した内容
Value1 val["cell"] 例:"C5" - settings.jsonに定義
Value2 val["value"] 例:"あり" - settings.jsonに定義

ラズパイ側の設定

電プチへの対応 - ramディスク化

電プチとは、電源引っこ抜き・電源コード引っこ抜きを指します。
持ち運びをできるようにするため、ラズパイをramディスクの有効化します。

  • ramディスクの有効化
$ sudo raspi-config nonint enable_overlayfs
$ sudo reboot

→再起動後、ramディスクが有効化されます。

  • ramディスクの無効化
$ sudo raspi-config nonint disable_overlayfs
$ sudo reboot

→再起動後、ramディスクが無効化されます。

起動時の動作 - crontab

crontabは定期的にジョブを実行するようスケジュールするcronを設定するコマンドです。
@rebootを指定することで、起動時に実行できます。

$ crontab -e

にて、編集を行います。

↓のように指定します。

@reboot python3 /home/pi/cron.py

タグの装飾-ネットワークプリント

タグを装飾するために、シールを作成しました。

https://networkprint.ne.jp/sharp_netprint/ja/top.aspx

ローソンで印刷しました。
↓こんな感じです。

さいごに

とりあえず、思いついたことが形にすることができてよかったです。
足りないものがネット越しに確認できることで、ネット通販などでも効果が発揮しそうです。
あとは、スーパーやホームセンターがいった場合に、自動で通知する仕組みまでつくれれば、十分かなという気がします。

GitHubで編集を提案

Discussion

ログインするとコメントできます