郵便受けと会話するためにobnizとLINE BOTでIoT装置を作ってみた 

# ■はじめに
郵便受けを実際に見に行かなくても投函物の有無を確認できるIoT装置を初心者が奮闘しながら作成してみました。
IoT初心者の方でも読みやすくなるような執筆に努めていますが、なにぶん初心者の文書になりますので、温かい目で読んでいただければ嬉しく思います。

# ■こんなもの作りました
郵便受けの底に重量センサー（ロードセル）を置き、プログラムを実行しておくことで、LINE BOTから投函確認されたときに投函有無を返答するものを作りました。
0gより軽いときに投函確認すると「投函物はありません」と返答してくれ、
0g以上のときに投函確認すると「投函物があります」と返答してくれました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/ff5a71b9-0f35-e64b-70b5-bc85578c0864.png" width=100%>

https://youtube.com/shorts/XT1WT0I6q4o?feature=share

動画は以下の順で確認したものになります。
① 投函物がない状態での確認
② 投函物がある状態での確認
③ 投函物を取り除いた状態での確認

# ■必要なもの
**◆ハード**
・obniz
・ロードセル（重量センサー）
・ADコンバーター

**・obniz**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/81f398de-4440-84b4-7bfc-508e49ceef26.jpeg" width=50%>
Node.jsの実行環境を用いた開発手順は以下を参照。
<a href="https://obniz.com/ja/doc/guides/nodejs/how-to-run">Node.jsで動かすには - obniz Docs</a>

**・重量センサー（ロードセル、ADコンバーター）**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/4b295553-0a98-e9ff-1920-2ba402486ebd.jpeg" width=50%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/d9f402e1-60be-72db-9bdb-c15cf6106b0c.jpeg" width=50%>
ロードセルを用いた重量測定器の準備は以下を参照。
<a href="https://qiita.com/iizuka2019/items/fb0307ec8e6bdc4ec689">重量センサ―で在席確認 - Qiita</a>

**◆事前準備**
・LINE公式アカウント 

**・LINE Messaging API（LINE BOT）**
LINE公式アカウントの作成方法、Messaging APIの設定方法は以下を参照。
<a href="https://qiita.com/YSFT_KOBE/items/8dc62ac40c5112df2ed3">Line Messaging APIで簡単Line Bot作成（超初心者向け） - Qiita</a>
※記載のAWSサービスは本記事では使用していないものになります。 

# ■詳細
**１．郵便受け製作**
段ボールで簡易的に郵便受けを製作しました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/b1b1bbd3-a502-cf7f-8340-34e0f5dafb5d.png" width=50%>

**２．重量センサー部分の工作**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/20b4c2fa-2634-a15b-5672-3a1b5348e4d7.jpeg" Width=50%>
**① はんだ付け作業**
ロードセルとADコンバーターを接続させる。

**② ２枚の板の間にロードセルをかませる**
以下２点の観点から郵便受けの底面と同じサイズの板を２枚用意して、板とロードセルの間にスペースができるようにして固定した。
（写真では上側がボルト、下側がワッシャーでスペースを確保している。）
　・投函物のサイズに影響を受けないようにする
　・ロードセルに的確に重量を計測させる（しなり易くする）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/fb1e7c5b-506f-8b56-24cc-577c95700337.png" width=60%>

**３．重量センサーの初期化と調整（センサーとobnizの連携）**
◆重量センサーの調整
ロードセルは変化量（しなり具合）を計測しているため、そのままではグラムを計測できません。そのためキャリブレーション（調整）が必要になります。
キャリブレーション方法は以下を参照。 
<a href="https://qiita.com/tishiyama/items/a025692aa8ab04a3ca11">重量センサーを使いたい - Qiita</a>

**４．LINE BOT（obnizとLINEの連携、プライベート環境と外部サーバを連携する）**
◆トンネリング通信を使った仕組み 
中段左側に郵便受けとパソコンの図があり、ここではプログラムを実行して、IoTツールを経由して郵便受けの投函状況を確認できます。
しかし、プログラムはローカル環境で実行されているため、このままでは外部との連携（LINEとの連携）が実現できていません。
そこで、スライド中央記載のトンネリングという技術を用いてローカル環境と外部サーバを接続させ、あたかもローカルにあるプログラムが外部サーバ上で実行されているような状態にします。
あとは、LINEに外部サーバのURLを設定するとLINEから郵便受けまでの経路ができあがります。
最後に作成したLINE BOTから会話を投げかけることで相互対話できるようになります。
ngrokを利用したトンネリング通信方法は以下を参照。
<a href="https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d">1時間でLINE BOTを作るハンズオン (資料+レポート) in Node学園祭2017 #nodefest - Qiita</a>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/41b5c75a-d055-86d1-b8ca-be2b3bd15911.png" width=100%>

**５．投函物の有無確認（LINEとスマホの連携）**
スマホ上のLINEでは以下のような画面になります。
ここでは、リッチメニューを作成して操作性を向上させています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820631/5e51deaa-b570-3c01-b472-fea0429fb3d5.png" width=40%>

# ■ソース
```javascript:app.js
'use strict';
// ########################################
//             Initialize Section
// ########################################
// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const Obniz = require('obniz');

// ngrokを介してローカル（自分のPC）でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;

const config = {
    channelSecret: '[secret]',
    channelAccessToken: '[AccessToken]'
};

//obniz APIを利用
const obniz = new Obniz("[obniz ID]");

//変数
var strValue = 0; //センサーからの取得値
var conv = 0; //取得値をグラム単位に変換用

// ########################################
//               obniz Section 
// ########################################
obniz.onconnect = async function () {

    console.log('重量センサー接続開始');
    const hcsr04 = obniz.wired("hx711", { gnd: 0, dout: 1, sck: 2, vcc: 3 });
    console.log('重量センサー接続完了');

    //ここの値でキャリブレーションする
    await hcsr04.zeroAdjustWait();
    hcsr04.offset = 266200;
    hcsr04.scale = 1000;

    while (true) {

        const val = await hcsr04.getValueWait(1);

        conv = Math.floor(val / 1000);
        console.log('weight:' + conv + 'g');

        await obniz.wait(1000);
    }

}

// ########################################
//      LINE Messaging API  Section
// ########################################
const sampleFunction = async (event) => {
    // ユーザーメッセージが「投函確認」かどうか
    if (event.message.text !== '投函確認') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: '「投函確認」と話しかけてね'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: '調べています……'
        });

        let pushText = '';
        var strWeight = '';
        if (conv >= 0) {
            strWeight = '投函物があります。'
        } else {
            strWeight = '投函物はありません。'
        }

        try {
            //返信用のテキストを作成
            pushText = `${strWeight}`;
        } catch (error) {
            pushText = '検索中にエラーが発生しました。ごめんね。';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        // 「プッシュ」で後からユーザーに通知します
        return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText
        });
    }
};

// ########################################
//  LINEサーバーからのWebhookデータを処理する部分
// ########################################
// LINE SDKを初期化します
const client = new line.Client(config);

// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
    // 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
    if (event.type !== 'message' || event.message.type !== 'text') {
        return Promise.resolve(null);
    }
    // サンプル関数を実行します
    return sampleFunction(event);
}

// ########################################
//          Express Section
// ########################################
// expressを初期化します
const app = express();

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {

    // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
    if (req.body.events.length === 0) {
        res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します（なくてもよい）
        console.log('検証イベントを受信しました！'); // ターミナルに表示します
        return; // これより下は実行されません
    } else {
        // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
        console.log('受信しました:', req.body.events);
    }

    // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
    // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
    Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});

// 最初に決めたポート番号でサーバーをPC内だけに公開します
// （環境によってはローカルネットワーク内にも公開されます）
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
```

# ■最後に 
ネットで提供されているソースコードそのままでは動かず、いろいろな記事を検索してなんとか動くようになりました。
カメラモジュールを使って中身を確認できる機能を実装したいと思っていましたが、今回は断念となりました。次回はチャレンジしたいと思っています。