ついつい水換えを忘れてしまいがちな私にアラートしてくれる”まりも”のスマートホームづくり

## 作ろうと思ったきっかけ

私、今、**まりもを飼っていて定期的に水換えをしています**。夏は2週間に1回程度、冬は1か月に1回程度。頻度はそれほど多くはないのですが、**気を抜くとついつい忘れてしまいがちです**。まりもは植物なので”早く水換えしてよっ！”って怒ってくることもなければ、歩いて自身で水換えすることもないのですが、でももしかしたら...。言わないだけで(私が水換えを忘れたとき)**本当はストレスを感じているのかもしれません**。もしかしたら気づかないだけで**実は無言の圧力を発しているのかもしれません**。ストレスを感じさせないためにも、私が全面的に気を遣ってあげる必要があります。水換えタイミングまであとどのくらいか教えてくれたり、**水換え時期をLine通知してくれるBotがあれば**、水換えを忘れることもなくなり、**私もまりもももっと快適に暮らせるのかもしれない**なって思いました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217319/bc56e1ef-66de-f63f-4d3a-db40f9ae563c.png)

## 仕組み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217319/8fab7c7f-d220-c0eb-2949-6561f478bfdb.png)


・**超音波センサを使って**まりもの**水換え中かどうかを検出** 
　　(センサ値100㎜以上：センサの前にいない≒水換え中)

・水換えが終わり試験管が**センサの前にセットされたタイミングの時刻を取得** 。
  さらに**1 sec置きに超音波センサ値取得し、経過時間も併せて算出**。

・最終水換えからの経過時間が基準(最終的には2週間を予定。今回は、実験目的なので、10secに設定）
　を超えたとき、**黄色のLEDを光らせて水換え時期であることを知らせる**。
　さらにその上の基準時間(最終的には4週間を予定。今回は、実験目的なので、20secに設定）
　を超えたとき**赤色のLEDを光らせ、ブザー音を鳴らす。。加えてサーボを回して、
　目に見える心の壁をつくり水換えしなかったことに対する怒りを表現**。 


## 材料
・ヘアゴムスタンド (ダイソーで購入)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217319/766686c5-e54d-5529-f6f5-325e5441e966.png)
・コルク栓付き試験管 (ダイソーで購入)
・超音波距離センサー　(HC－SR04) 
・サーボモータ (SG90)
・obniz Board 1Y
・マイコン内蔵RGB　8mmLED　PL9823－F8
・タイラップ
・厚紙


## 配線
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217319/375eb3df-a874-28d9-882d-950f12a637b3.png)

## 組立後の様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217319/b6130276-04d9-bf07-cb5c-2337018b875b.png)

## 環境
・Github codespaces
・node.js (ver 19.9.0)
・line/bot-sdk　(ver 7.5.2) (使用予定)

## ソースコード
<details><summary>コードはコチラ</summary>

```javascript
'use strict';

const Obniz = require('obniz');
const obniz = new Obniz('******'); // Obniz_IDに自分のIDを入れます

obniz.onconnect = async function () {
  // 接続設定
  const hcsr04 = obniz.wired('HC-SR04', { gnd: 0, echo: 1, trigger: 2, vcc: 3 });
  const servo = obniz.wired('ServoMotor', { signal: 4 });
  const rgbled = obniz.wired('WS2811', { gnd: 5, vcc: 6, din: 7 });
  const speaker = obniz.wired('Speaker', { signal: 9, gnd: 11 });

  let degrees = 90.0 //サーボの角度

  // ディスプレイ
  obniz.display.clear(); // クリア
  obniz.display.print('Hello obniz!');

  let startTime = new Date(); // 開始時間

  // setIntervalで一定間隔で処理
  setInterval(async function () {
    // 距離を取得
    let distance = await hcsr04.measureWait();
    // そのままだと小数点以下の桁数がやたら多いので整数に丸める
    distance = Math.floor(distance);

    // 距離(mm)をターミナルに表示
    console.log(distance + ' mm');
    // obnizディスプレイに表示
    // 一度消してから距離+mmの単位を表示
    obniz.display.clear();
    obniz.display.print(distance + ' mm');

    let currentTime = new Date(); // 現在時間
    let currentHour = currentTime.getHours() +9 //時間(h)の取得
    currentTime.setHours(currentHour); 
    console.log(currentTime);
    console.log(currentHour);

    // 試験管の有無を距離で判定
    if (distance < 100) { // まりも試験管があるとき約70mm ：ただし、試験管自体が曲率があるので置く位置でかなりばらつくのでマージン持たせている。
      // まりもがいるとき
      obniz.display.clear();
      obniz.display.print('Presense!!');

      let elapsedTime = Math.floor((currentTime - startTime -9 * 60 * 60*1000) / 1000); // 経過時間（秒）startTime: 9時間補正
      console.log(elapsedTime);

      if (elapsedTime <= 10){
        rgbled.rgb(0, 0, 255);
        servo.angle(90);
      }
      else if((elapsedTime > 11)&&(elapsedTime <= 20)){
        rgbled.rgb(125, 125, 0);
        servo.angle(90);
      }
      else if((elapsedTime > 21) &&(elapsedTime <= 25)){
        rgbled.rgb(255, 0, 0);
        speaker.play(1000);
        servo.angle(180);
      }
      else if(elapsedTime > 25){
        speaker.stop();
      }
      
      obniz.display.print(elapsedTime + ' sec');
        
    } else { //水換えのとき
      obniz.display.clear();
      obniz.display.print('Absense!!');
      startTime = new Date(); // 開始時間のリセット(水交換した時刻を基準)
      rgbled.rgb(0, 0, 0);
      speaker.stop();
    }
  }, 1000); // 1000ミリ秒 = 1秒おきに実行
}
```
</details>

## 動作の様子

<iframe width="920" height="690" src="https://www.youtube.com/embed/sNL4ZgFZ4OY" title="”まりも”のスマートホーム" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>






## 作ってみて思ったこと
LINEとの連携試みましたが、思った以上に難しくて時間がかかってしまったので、まずは形になったところまでを共有しました。本記事投稿前に今回作ったものを見て頂いたのですが、そのとき怒りの感情だけを表現するのではなく、例えば何かの記念日(自分とまりもが出会った日など)に感謝を伝えるなどあると、もっとよくなるかもとコメント頂きました！とても良い案だと思いました。手を動かしていると、ついついのめり込んでしまって一人で視野が狭くなってしまいがちになるのですが、まずは出来るところまでを形にして、それを色々な人に見て頂くと、よりよいものが出来そうって思いました。まだ未完成なので、引き続き製作を進めるとともに、マインド面も大切にしたいなって思いました。

## 実をいうと...。
今回の記事投稿までには、うまくいきませんでしたが、LINE連携部分、APIを叩くと何かしらJSONデータは帰ってきました。途中で落ちるのですが。格闘の爪痕くらいは残しておきます。。もう少しあがきますが、もし詳しい方でココ間違ってる、こうしたらいいよとかあればアドバイス頂けるとありがたく思います。

<details><summary>LINE連携の爪痕コード</summary>

```javascript
const Obniz = require('obniz');
const line = require('@line/bot-sdk');

const obniz = new Obniz('*****'); // Obniz_IDに自分のIDを入れます

const lineConfig = {
  channelAccessToken: '*****',
  channelSecret: '******'
};

const client = new line.Client(lineConfig);

let startTime; // 開始時間

obniz.onconnect = async function () {
  // 接続設定
  const hcsr04 = obniz.wired('HC-SR04', { gnd: 0, echo: 1, trigger: 2, vcc: 3 });
  const rgbled = obniz.wired('WS2811', { gnd: 5, vcc: 6, din: 7 });
  const servo = obniz.wired('ServoMotor', { signal: 4 });

  let degrees = 90.0; //サーボの角度

  // ディスプレイ
  obniz.display.clear(); // クリア
  obniz.display.print('Hello obniz!');

  startTime = new Date(); // 開始時間の初期化

  // setIntervalで一定間隔で処理
  setInterval(async function () {
    // 距離を取得
    let distance = await hcsr04.measureWait();
    // そのままだと小数点以下の桁数がやたら多いので整数に丸める
    distance = Math.floor(distance);

    // 距離(mm)をターミナルに表示
    console.log(distance + ' mm');
    // obnizディスプレイに表示
    // 一度消してから距離+mmの単位を表示
    obniz.display.clear();
    obniz.display.print(distance + ' mm');

    let currentTime = new Date(); // 現在時間
    let currentHour = currentTime.getHours() + 9; //時間(h)の取得
    currentTime.setHours(currentHour);
    console.log(currentTime);
    console.log(currentHour);

    //日中は遮光カーテンを開け、夜は遮光カーテンを閉める。
    if (6 < currentHour && currentHour < 19) {
      servo.angle(90);
    } else {
      servo.angle(180);
    }

    // 試験管の有無を距離で判定
    if (distance < 100) {
      // まりもがいるとき
      obniz.display.clear();
      obniz.display.print('Presense!!');

      let elapsedTime = Math.floor((currentTime - startTime) / 1000); // 経過時間（秒）
      rgbled.rgb(0, 0, 255);
      obniz.display.print(elapsedTime + ' sec');
      //console.log(currentTime.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }));

      // Lineへの経過時間の送信
      if (elapsedTime >= 0) {
        const message = {
          type: 'text',
          text: '経過時間: ' + elapsedTime + '秒'
        };
        // メッセージの送信先がLineであることを示すために、コメントアウトされた部分を追加してください。
        // 以下の行はコメントアウトされているので、コメントを外して追加します。
        //client.replyMessage(event.replyToken, message);
        client.pushMessage('USER_ID', message); // USER_IDにはメッセージを送信したいユーザーのIDを指定してください
      }

    } else {
      //水換え中のとき
      obniz.display.clear();
      obniz.display.print('Absense!!');
      startTime = new Date(); // 開始時間のリセット(水交換した時刻を基準)
      rgbled.rgb(255, 0, 0);
    }
  }, 1000); // 1000ミリ秒 = 1秒おきに実行
};

const express = require('express');
const PORT = process.env.PORT || 3000;
const app = express();

app.use(express.json()); // JSONパースのためのミドルウェア

app.post('/webhook', line.middleware(lineConfig), (req, res) => {
  Promise.all(req.body.events.map(handleEvent)).then((result) =>
    res.json(result)
  );
});

app.listen(PORT);
console.log('express running: PORT =', PORT);

async function handleEvent(event) {
  if (event.type === 'message' && event.message.type === 'text') {
    if (event.message.text === '経過時間を教えて') {
      let currentTime = new Date();
      let elapsedTime = Math.floor((currentTime - startTime) / 1000);
      const message = {
        type: 'text',
        text: '経過時間: ' + elapsedTime + '秒',
      };
      await client.replyMessage(event.replyToken, message);
    }
  }
}
```
</details>




## 参考記事

・**トイレの蓋を閉め忘れ彼女にブチギレられたので、obnizを使って解決しにいった話**
　(https://qiita.com/atoms50/items/81a401fa705937746900)
  
　→ LEDと圧電スピーカ、超音波センサだけでこんなにも面白いデバイスが作れるんだって、
　  感銘を受け、私も作ってみたいってインスパイアされた記事です。 

・**会社の机に置くための観葉植物を作りました**
　(https://t.co/Uyaj7MYiF2)
　→ 思っていることとを伝える方法は言葉だけではないんだなと非常に参考になりました。
　 私も今回作品をつくるに当たり、まりもという本来意思を持たない？植物が意思を持ってたら
　 どんな風に伝えるだろうって考えるきっかけになった記事です。