家のポストに投函されたらLineに通知するIoTデバイスの作成

# 1. はじめに

我が家は、外の門扉に郵便受けポストがあり、ポスト投函された物の確認は、玄関から外に出る必要がある。受け取りが待ち遠しい時は、何度も門扉まで行ったり来たりで無駄が多い。
もちろん、「配達完了メール通知サービス」などを利用すれば、メールが知らせてくれるのだが、そのサービスが使えないことも多々ある。
そこで、郵便ポストに仕掛けをして、ポスト投函されたらLineに通知するIotデバイスを作成した。


# 2. Line通知方法

外の門扉の郵便ポストだが、ギリギリ屋内のWiFiにつながる距離であるため、以前作成したこちらの記事[『ドアホンが鳴ったらLINEに通知する』](https://qiita.com/nak435/items/879b647aa3d7343ba0be)と同様の方法で実現する。ただし、今回はESP32-C3-WROOM-02（ESP32-C3）を使うことにした （ESP32-S3より省電力だろうと勝手に解釈）。

<img width="200" alt="M-17493.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/f5083b06-1515-7207-4450-6f4fd0f0cb20.jpeg" />

ESP32-C3を、↓こちらの[実装ボード](https://orijinalj.booth.pm/items/4495391)に載せて使う。

<img width="300" alt="e84521bd-310a-4a79-842b-4b1e6e576941_base_resized.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/f44c5b28-3dd7-205d-9267-a46180dd4bd2.jpeg" />

この実装ボードの良いところは、使う目的によって、使わない部分の基板を切り捨てることができるように設計されているところ。今回は、3V電源を直接入力するため、基板下側のUSBコネクタと3.3V電源レギュレータは不要なため、切り離し可能（面倒なので基板切断はせず、今回はパーツの実装を省略しただけ）


![79e99d13-7851-4ce6-8291-545748a6d1b4_base_resized.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/a6accf19-2f7a-a8e3-9022-c368770d801b.jpeg)

_省電力化のため基板上の電源LEDは今回は実装しない。_

# 3. ポスト投函を検知する


<img width="100" alt="sw-18010p.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/1563aafd-962f-1fae-b703-63d827552a0e.png" />

今回は↑こちらの[振動センサーSW-18010P](https://www.aitendo.com/product/3192)を使用した。

（稚拙な絵で申し訳ないが、）下のイラストで示すように、ポストの扉の付け根あたりに振動センサーを取り付け、その信号をESP32のGPIOで受け取り、`ポストされた`と検知したらLineに通知するという流れである。

_ポストの扉（蓋）は結構な強さのバネで閉じた状態を維持しており、指でグイッと内側に押し込まないと扉が開かずポスト内に物が入れられない。よって、大きな振動が発生するはずだ。_

![ポスト投函検知.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/c040b41f-f6b2-6b6c-fe10-59464d317e14.png)


# 4. 振動センサーSW-18010P の下調べ

Arduino UNOを使い、SW-18010Pの性能を調べる。

振動させる向や強さで、スイッチONやOFFになるタイミングとスイッチONの持続時間などを調べる。


<details><summary>検証に使用したコード（クリックで開く）</summary>

```c:sketch.ino
const int SW_pin = 7; //D7

void setup() {
  Serial.begin(115200);
  pinMode(SW_pin, INPUT_PULLUP);
  Serial.println("Waiting for 10s...");
  delay(10 * 1000); //wait 10s
  Serial.println("Ready!");
}

void loop() {
  static bool first = true;
  static int on_count = 0;
  static int sw_status = -1;
  static unsigned long last_time = 0;

  int isOn = (digitalRead(SW_pin) == LOW);
  if (sw_status == isOn) return;

  on_count += 1;
  sw_status = isOn;
  auto now = millis();
  auto duration = first ? 0 : now - last_time;
  last_time = now;
  first = false;

  char buff[128];
  sprintf(buff, "[%d] SW status change to %s (%lu)", on_count, (isOn ? " On" : "Off"), duration);
  Serial.println(buff);
}
```

</details>


このセンサーは、平常時OFF、振動を加えるとON-OFFを繰り返す動作となった。どの方向でも平常時はOFFなので、スイッチとして扱いやすい。
振動を検知する方向としては、下の図で示すところのX軸方向やY軸方向の振動に対し敏感で、Z軸方向の振動に対しては、鈍い傾向であった。

![xyz2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/1d8fa75f-5029-7a44-ba49-13d600ff1bdb.png)



# 5. バッテリー駆動させる

屋外のポストに設置するため、バッテリー駆動させる前提である。
データシートによるとESP32-C3-WROOM-02の稼働電圧は、3.0V〜3.6Vとなってる。実際に3.0Vで問題なく稼働しWebReplにも反応する。


データシートによると、`DeepSleep`中の待機電流は5μAとのこと。

後ほど完成した装置で実際の消費電流を計測するとする。


# 6. 配線図


<img width="300" alt="cir2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/7c3686a7-09e7-d10b-4af4-574bd3fc5529.png" />


1. GPIO0 - スライドSW - GND　（デバッグ用：DeepSleep抑止）

1. GPIO1 - LED - GND

1. EN - 振動センサ - GND

1. 3V3 - 単1乾電池x2本直列 - GND

1. TX, RX, GND ： USBシリアル　（デバッグ用：回路図上は省略）

当初、DeepSleepからの復帰にはGPIO4を使い、GPIO4をLOWにしたら、復帰するように考えていたが、下記のコードでは復帰しなかった。ネットで色々調べたが、事例はあるが回避策が分からず諦めた。

```python:MicroPython v1.20.0 (2023-04-26)版
import machine
import esp32
wake = machine.Pin(4, machine.Pin.IN)
esp32.wake_on_ext1((wake,), esp32.WAKEUP_ALL_LOW)
machine.deepsleep()
```

代わりにENをLOWに落として、再起動するようにした。ソフトウェア的には違いが無い。

_おそらくC言語（ArduinoやESP-IDF）でプログラムすれば動作するのではないかと思うが、試していない。MicroPythonの実装上の不備と想定する。_


# 7. Pythonコード

今回もMicroPythonを使う。
`boot.py`でWiFiに接続している前提で、`main.py`が呼ばれたら、それは`ポスト投函`によるDeepSleepからの復帰であるため、Lineに通知して再びDeepSleepするだけだ。

```python:main.py
from machine import Pin, RTC, Timer, reset, PWM, deepsleep
import time
import ntptime
import urequests as requests
import ujson as json

rtc = RTC()
ntptime.host='ntp.nict.jp'
ntptime.settime()

WEEKDAY = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
UTC_OFFSET = 9 * 60 * 60   # JST +09:00

# GPIO0 : SW
sw = Pin(0, Pin.IN, Pin.PULL_UP) 
#sw.value() # 0: on, 1: off

#GPIO1 : LED
led_pin = Pin(1, Pin.OUT)
#led_pin.value(1)
led = PWM(led_pin, freq=1000, duty_u16=32768)
led.duty(50)

# notify to Line
def send_message(message):
    t = time.localtime(time.time() + UTC_OFFSET) 
    timestamp = ('{0}.{1}.{2}[{6}]{3}:{4:02}:{5:02}').format(t[0], t[1], t[2], t[3], t[4], t[5], WEEKDAY[t[6]])
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer XXXXXXXXXXXXXXXXXX',
    }
    data = 'message={}%0D%0Aat%20{}'.format(message, timestamp)

    response = requests.post('https://notify-api.line.me/api/notify', headers=headers, data=data).json()
    if response['status'] != 200: print(response)


send_message('Notify for Posted.')

if not sw.value(): deepsleep()
```

_起動状態を示すLEDだが、GPIO1に接続したLEDが眩しかったので、PWM制御に変更して暗めにした（少しでも電池の消費を抑えるため）。DeepSleepに移行するとすべてが停止してLEDも消えるため、LEDの点灯時間はほんの数秒だ。_


# 8. 机上検証

通常時（WiFi接続時）とDeepSleep時の電流値を測定して、乾電池での稼働日数を計算したいのだが、手持ちの安いデジタルテスタだと、内部抵抗が大き過ぎるのか、乾電池では起動しなくなる。・・・別途考えるとする。

まずは、しばらく今のままで運用してみてます。

**以上**


# 9. 乾電池寿命? （2023.9.2追記）

運用開始して数日は、新聞等の投函で毎日通知が来ていましたが、ある日から通知が来なくなりました。外して原因を調べたところ、乾電池の電圧が2.8Vまで下がっていました。
ESP32-C3-WROOM-02のデータシートによると、`Power supply voltage`は3.0V〜3.6Vとなっているので、2.8Vでは起動しませんね。

そこで、3.3V昇圧回路を入れて、運用することにします。秋月電子製の[3.3V出力昇圧ＤＣＤＣコンバーター](https://akizukidenshi.com/catalog/g/gK-13066/)昇圧回路を使いました。
<img width="100" alt="K-13066.jpg" src="https://akizukidenshi.com/img/goods/L/K-13066.jpg" />

これを使っても、単三電池x2本直列では、安定した起動ができませんでした。3.3Vは出ますが、おそらく電流不足だと思います。そこで、単一電池x2本直列で運用します。

また、しばらくこれで運用してみてます。

**以上**


<!-- 

仮のボードを組み立て、単3電池x2本直列接続の3Vで動作確認を実施。振動センサを揺すると、DeepSleepから目覚めLineに通知が来た。
時間にして、振動センサを揺すってから8秒ちょっとでLineに通知が出来、9秒になる前に再びDeepSleepに入る。

電流値の測定の結果、稼働時で80mA弱、DeepSleep時で9.5mAであった。

:::note 
アルカリ単3電池の容量を1,300mAhとして、
24時間連続稼働、1日2回ポスト投函を検知する場合、
ポスト投函を契機にDeepSleepから目覚め、WiFi接続・Lineへの通知後、再びDeepSleepする。起きている時間を1回10秒電流90mAとして、DeepSleep時10mAとして、

起きている1回あたりの消費電流：10秒×100mAh≒0.278mAh
DeepSleep中の消費電流：10mAh
24時間あたりの消費電流：10mAhx23.994/日+0.278mAhx0.006/日＝239.94mAh/日
アルカリ単3電池で何日動く：1,300mAh÷240mAh/日≒5.4日

5日しか持たない??
:::

-->