編集履歴一覧に戻る
ftakeのアイコン画像

ftake が 2021年04月14日21時48分16秒 に編集

LINE Notify 版

記事種類の変更

+

製作品

本文の変更

はじめに ==================== リモートワークで外出しない日々が続くと、郵便受けに郵便物があるかどうかをチェックするのが遅れがちな今日この頃です。そこで、郵便受けに郵便物があることを通知する装置を作ってみました。 郵便物の確認というと、例えば次のような方法がよく出てきます。 * 郵便受けの扉部分が動いたことを検知する * カメラで内部を撮影して、画像認識する * 重さを測ることで郵便物があることを検知する 今回はシンプルに郵便受けの底面に設置したフォトリフレクター(赤外線を照射して、反射して返って来るとトランジスタに電流が流れるセンサー)を使って、フォトリフレクターの上に郵便物があることを検知することで実現することにしました。この方法では、どのような郵便物が入っているかは分かりませんが十分でしょう。高性能なマイコンボードや、高価なセンサー、大容量の電池が要らないメリットもあります。 ![郵便物を検知する仕組み](https://camo.elchika.com/e0ad5512348cabc8923a69420619dd0d13af6a14/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f61656235636536652d623163642d346633362d383362652d373337346239346330393230/) また、郵便受けは集合型で自室の外にあり、検知結果を無線で通知する必要があります。そこで、Bluetooth Low Energy (BLE) で飛ばして、室内に置いた Raspberry Pi でキャッチすることにしました。金属の箱の中からでも意外と届くものですね。(ただし、Raspberry Pi を郵便受けから近く電源も取れるトイレの窓際に設置) 今回は、基本的な仕組みの動作検証ということで、通知はターミナルに出すだけです。Raspberry Pi まで来てしまえば、IFTTT 経由で LINE にメッセージを送ったりとか、なんとでもできますね。連続稼働時間についても実用面では今後検討ですが、M5 Stack でどこまでいけるかを試します。 材料 ======================= * M5 Stack Gray: 手元にあったので…。連続稼働時間を考えると変えた方がよさそう。 * フォトリフレクタ コーデンシ SG-105: 検知可能距離が短いのでこちらを選びました。 * 抵抗: 750, 470 k(調整必要) * 電源が自動で切れないモバイルバッテリ(ノベルティでもらったもの) * 適当なユニバーサル基板、銅線 * 自己融着テープ * 段ボール 作り方 ========================== 回路定数決め --------------------------------------------------- ![ブレッドボード上で調整](https://camo.elchika.com/4b2d33d73ab14ae38f6d1bd6c4f391b20d515129/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f38616561626263312d333336342d343138352d623363612d643439356232303533363338/) まずは、ブレッドボードで実装して回路定数を決めます。電源は 3.3 V を使います。Vcc からではなく、GPIO 5 から電源を供給しているのは、測定時以外は OFF できるようにするためです。LED の抵抗はスマホのカメラで点灯しているのを確認しながら大きな抵抗にしていきます(数 mA で点灯するので、だいたいこのくらいの抵抗ですね)。抵抗を小さくし過ぎると LED が焼き切れたり、消費電力が増えるのはもちろんですが、天井から反射して返って来る量が増えるのも注意が必要です。 次に、重要なセンサー値の読み取りです。A/D 変換を使ったり、可変抵抗をつければ後から調整することもできるのですが、シンプルに GPIO で取り込むことにしました。そのためには郵便物があるときに、GPIO 2 の電圧がプログラムから読み取ったときに1になる電圧になるように R2 の抵抗値を調整します。反射しにくい濃紺の(某宅配レンタルの)封筒をセンサーの上にかざした状態で、R2 を増やしていった結果、750 kΩ になりました。 ![回路図](https://camo.elchika.com/d03f6491b4b5b6ecbeeb7cc1463b55c860e610a2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f38376339346365352d306537642d343239632d383736382d363765343363636134616532/) 実装 ----------------------------------------------------- ユニバーサル基板でこのような感じに実装しました。この配置で大丈夫?と思う方もいるかと思いますが、久しぶりの電子工作で、半田付けしている最中に配線間違いに気づいて裏側は残念な感じになっています(一応ノージャンパー)。 ![ユニバーサル基板での実装](https://camo.elchika.com/1313a8aac6436d2821d1a0c18e8a15f2de59f382/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f37343461666665632d333635622d343463332d396635642d393666363338666561323062/) 郵便受けは金属なので自己融着テープで簡単に絶縁しました。 ![絶縁した基板](https://camo.elchika.com/816269855f6e4c2878819fee3e6e77d21ddb0158/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f30653430353661392d343831342d343037632d383036662d366536333733323430663363/) 郵便受けの床の部分は段ボールで作りました。真ん中に穴を開けて取り付けます。フォトインタラプタは対象物から多少離れていた方が感度が良いため、床面から少し離したほうが良いです。段ボールの穴のサイズも余裕を持っておかないと、赤外線がフチで反射する可能性があります。 ![組み立て後](https://camo.elchika.com/17b151df22fac90b19ecccf43585e634316e5f7e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62656137353162662d646535382d343265342d623863662d3336636561346235366365652f37393563393030362d383535342d343665612d386537312d376462383139316362666437/) M5 Stack のプログラミング ---------------------------------------------------- M5 Stack で BLE は色々な人が実践していますが、今回はこちらを参考にしました。 https://pages.switch-science.com/letsiot/bleperiph/index.html 今回は、BLE でもブロードキャストモードを使用しています。ブロードキャストモードは自身をアドバタイズする(デーバイス名などを通知する)ときに、データを送る方法で、この場合は郵便物の状態を配ります。受信するデバイスとはペアリングしたり、コネクションを確立する必要はありません。ただし、ポストの状態は周りに丸見えになってしまうので注意が必要です。 このプログラムでは 30分に1回起きて、フォトリフレクタの赤外線を点灯して郵便物の有無をチェックします。その後、10秒間の間 Bluetooth で郵便物の状態を送ります。普通の Bluetooth デバイスはスマートフォンなどのデバイスからスキャンすると、これに応答する形でアドバタイズします。このプログラムでは M5 Stack 側から勝手に送るので、このスキャンに応答する必要がありません(`setScanResponse(false)`)。 ```c++ #include <Arduino.h> #include <M5Stack.h> #include <BLEDevice.h> #include <BLEServer.h> static BLEServer *pBLEServer; static void setup_ble() { BLEDevice::init("Post Notifier"); pBLEServer = BLEDevice::createServer(); } void setup() { M5.begin(); Serial.begin(115200); pinMode(GPIO_NUM_2, INPUT); pinMode(GPIO_NUM_5, OUTPUT); // スピーカーとディスプレイを止めておく pinMode(GPIO_NUM_25, OUTPUT); M5.Lcd.writecommand(ILI9341_DISPOFF); M5.Lcd.setBrightness(0); setup_ble(); } static void advertise_data(char val) { BLEAdvertisementData advertisementData {}; advertisementData.setFlags(0x06); std::string data; data += (char)0x04; // Length data += (char)0xff; // AD Type: Manufacture Specific data += (char)0xff; // Company Identifier Code data += (char)0xff; // Company Identifier Code data += val; // Specific data advertisementData.addData(data); BLEAdvertising *pAdvertising = pBLEServer->getAdvertising(); pAdvertising->setAdvertisementData(advertisementData); // Passive スキャンを使うので、スキャンに応答不要 pAdvertising->setScanResponse(false); pAdvertising->start(); delay(10 * 1000); pAdvertising->stop(); } void loop() { digitalWrite(GPIO_NUM_5, HIGH); delay(50); auto input1 = digitalRead(GPIO_NUM_2); digitalWrite(GPIO_NUM_5, LOW); Serial.printf("%5d\n", input1); advertise_data(input1); M5.Power.lightSleep(SLEEP_MIN(30)); } ``` Raspberry Pi 側のプログラミング ------------------------------------------------------------------------

-

Python で 10 秒タイムアウトで繰り返しスキャンを実行し、M5 Stack からのデータを拾います。りあえずコンソール状態を書ことで今回は良しとします。

+

Python で 10 秒タイムアウトで繰り返しスキャンを実行し、M5 Stack からのデータを拾います。BLE へのアクセスは [bluepy](https://github.com/IanHarvey/bluepy) いうライブラリで簡単。bluepy の説明は世の中にたくさんありますので今回は詳細を省きます。

-

ポイスキャンでアドバタイズを要求せずに受信するパッシブモードを使っています。これにより、M5 Stack 側で Scan Response に応答する必要がなくなります。

+

スキャ `scan()` 関数を呼び出すだけです。前述の通り、スキャンでアドバタイズを要求せずに受信するパッシブモードを使っています。これにより、M5 Stack 側で Scan Response に応答する必要がなくなります。

+

`read_received_data()` はスキャンして受信したデータから目的のデータを取り出します。スキャンすると周辺の BLE デバイスから色々と受信してしまいますので、設置した M5 Stack からのデータを見つけ、さらにポストの状態を取り出します。Manufacture Specific の3バイト目にデータが入っていますが、16進数の文字列として返って来るため4文字目と5文字目が目的のデータです。

```python

+

import os import urllib.parse import urllib.request

from datetime import datetime from time import sleep

-

from bluepy.btle import BTLEException, ScanEntry, Scanner,

+

from bluepy.btle import BTLEException, ScanEntry, Scanner

+

def main():

-

# とりあえず、HW アドレス直書き device_addr = "xx:xx:xx:xx:xx:xx"

+

start_time = datetime.now() print(f"{start_time} Post Notifier GW")

-

print(f"{datetime.now()} Post Notifier GW")

+

last_updated = start_time prev_state = 0 no_signal_notified = False

scanner = Scanner()

+

# 環境変数から設定の読み込み # デバイスのHWアドレス device_addr = os.environ["DEVICE_ADDR"] # LINE Notify の開発者用トークン line_notify_token = os.environ["LINE_NOTIFY_TOKEN"]

while True: try:

-

# パッシブモード

entries = scanner.scan(10.0, passive=True)

-

for entry in entries: entry: ScanEntry if entry.addr == device_addr: data = entry.getValueText(ScanEntry.MANUFACTURER) val = int(str(data[4:6]), 16) print(f"{datetime.now()} status: {val}")

+

now = datetime.now()

+

received_data = read_received_data(entries, device_addr) if received_data is not None: # データ受信 print(f"{now} status: {received_data}") if prev_state != received_data and received_data: notify("郵便物が届きました", line_notify_token) prev_state = received_data last_updated = now no_signal_notified = False # 電池切れ通知 if not no_signal_notified and (now - last_updated).seconds > 60 * 60 * 2: notify("2時間以上受信していません", line_notify_token) no_signal_notified = True

except BTLEException as e: print(f"{datetime.now()} Error {e}") sleep(60)

+

def read_received_data(entries, device_addr): for entry in entries: entry: ScanEntry if entry.addr == device_addr: data = entry.getValueText(ScanEntry.MANUFACTURER) return int(str(data[4:6]), 16) return None def notify(msg: str, token: str) -> None: api_url = "https://notify-api.line.me/api/notify" headers = {"Authorization": f"Bearer {token}"} data = {"message": msg} request = urllib.request.Request( api_url, headers=headers, data=urllib.parse.urlencode(data).encode()) try: with urllib.request.urlopen(request) as response: print(f"{datetime.now()} [LINE Notify] msg: '{msg}'; response: {response.read()}") except Exception as e: print(f"{datetime.now()} [LINE Notify] Error {e}")

if __name__ == "__main__": main() ```

-

出力はこような感じす。16:45〜16:55 投函されたことが分かります。

+

郵便物を見つけたら LINE Notify を使って LINE に通知します。自分 LINE アカウントに送るだけあれば、[開発者サイト](https://notify-bot.line.me/ja/) で開発者用トークンを取得すれば簡単できます。`notify()` 関数が LINE に送るコードです。API を1回叩くだけです。

+

取得した開発者用トークンは環境変数の `LINE_NOTIFY_TOKEN` にセットしておいて下さい。 出力はこのような感じです。03:59〜04:28 の間に投函されたことが分かります。

```

-

2021-02-19 16:45:33.953899 status: 0 2021-02-19 16:45:43.978746 status: 0 2021-02-19 16:55:35.156646 status: 0 2021-02-19 16:55:45.176626 status: 0 2021-02-19 17:05:36.366733 status: 1 2021-02-19 17:05:46.387938 status: 1 2021-02-19 17:15:27.542689 status: 1 2021-02-19 17:15:37.563869 status: 1

+

2021-04-10 03:29:17.942892 status: 0 2021-04-10 03:29:27.965471 status: 0 2021-04-10 03:58:51.928888 status: 0 2021-04-10 03:59:01.953031 status: 0 2021-04-10 04:28:35.967972 status: 1 2021-04-10 04:28:36.165504 [LINE Notify] msg: '郵便物が届きました'; response: b'{"status":200,"message":"ok"}' 2021-04-10 04:28:46.197977 status: 1 2021-04-10 04:58:20.200485 status: 1

```

-

今後の計画

+

おわりに

===========================================

-

フォトリフレクタを使った郵便物チェッカーの基本部分を作ってみました。

+

フォトリフレクタを使った郵便物チェッカーを作ってみました。

現状と今後の改造についても書いておきたいと思います。

-

* もうちょっとマシな通知の仕組みを考える

* バッテリーの持ち: 9.25 Wh のモバイルバッテリーで4日ほど持ちます。残念ながら M5 Stack は待機時の消費電力が大きいようで、スリープを Deep sleep にしても、検知間隔を増やしてもバッテリーの持ちは変わりません。マイコンを変える必要があるようです。 * LPWA を使ってみる: 今回は BLE で届いてしまったのですが、当初の計画では Sigfox で飛ばしてみたいなと思っていました