僕たちは植物を愛でる
植物っていいですよね。
僕は緑が大好きで、よく芝生にゴロゴロしにいくのですが、
やはり植物に触れると心が癒されます。
しかしながら、仕事で忙しいとなかなか外出することができず、
最近はもっぱら自宅で観葉植物を育成し愛でております。
僕たちは植物を管理したくなる
obnizで植物を管理するプロダクトを調べるといくつかヒットします。
下の記事は室内の温度・照度を計測し、植物にとって快適な状態かを管理しているようです。
植物の生育状態をLINEで監視してみた
筆者は「植物がすくすく育つ喜びを感じて欲しい」という想いのもと制作されているそうです。
僕もその気持ちはとてもよくわかります。
IoTの力で植物の育成をもっと楽に・楽しくできたらいいなあというのが、今回の動機となります。
とはいえ、僕たちは植物を枯らしてしまう
はっきり言います。
僕たちが抱える最も大きな課題は、植物を枯らしてしまうことです。
どれだけ植物を愛でようが、IoTで温度・照度を管理しようが、
水やりを怠れば植物は枯れてしまいます。
枯れた植物は2度と愛でることはできません。
これは、僕たちが抱えるとても残酷な課題です。
かくいう僕は、最近リトルベイビーという飼育がシビアな植物を育てていました。
購入時、お店の方に「土が乾いたタイミングで水をあげてくださいね」と教えてもらいました。毎日のように土の湿度を確認して水をあげ大切に育てていました。
しかし、仕事が忙しい期間、ほんの1週間水をあげることができず、残念ながら枯らしてしまいました。
試しに、ガーデニング歴が長いプロにヒアリングをしてみると、
やはり彼らも人間なので、水やりを怠って枯らしてしまうことは多々あるそうです。
この問題は、素人・プロかかわらず発生してしまう根の深い問題なのではないでしょうか。
僕が解決してみせる
この、水やり不足で植物を枯らしてしまう問題に対して、IoT機器で解決をはかります。
今回製作した機能はこちらです
- 土の湿度の管理
・LINEでどこからでも植物の土の湿り度合いを確認することができる
・湿り度合いによって水のあげ時を教えてくれる - 遠隔で自動水やり
・土が乾いていた際に、LINEを介して遠隔から植物に水をあげることができる
これによって、
いつでもどこでも土の状況を管理することができますし、
遠出をして家に長期間帰れないような時にも植物に水をあげることができます。
完成デモ
①土の湿度の管理
LINEbotでメニューから「湿度を測る」を選択すると、現在の湿度と水やりの有無を教えてくれます。
②水やり
同様のメニューから「水をあげる」を選択すると、自動で水やりを行います。
実際の水やりの様子。
(注ぎ終わりが熟練のバーテンダーのようで個人的に気に入っています)
製作:環境
node v18.8.0
Visual Studio Code v1.71.0
axios 0.27.2
製作:obnizの準備
用意したもの
- obniz Board 1Y
- ミニブレッドボード BB-601(白)
- 土壌湿度センサー SEN0114
- TowerPro サーボ MG996R
土壌センサの仕様や、計測値の扱いはこちらのドキュメントを参考にしました。
機器の設置
サーボモータは台座(山頭火の歌集)に固定し、アーム部分に水入れ容器(クロレッツの空き箱)を固定する。
ちなみにプロダクトを製作した際のデスクはこんな感じでした。
(マジでどこに向かっているんだ、、、)
ソースコード
const Obniz = require('obniz');
const obniz = new Obniz('*******');
// obnizと接続確立したとき
obniz.onconnect = async () => {
obniz.display.clear();
obniz.display.print('obniz Ready');
}
// 湿度センサから値を取得して返す
const getObnizMois = async () => {
// 非同期で取得
const sensor = obniz.wired("SEN0114", {vcc:0, gnd:1, output:2});
const value = await sensor.getHumidityWait();
// ターミナル表示
console.log('obniz mois:', value);
// obnizディスプレイ表示
obniz.display.clear();
obniz.display.print(value);
// 湿度値を返す
return value;
}
//サーボモータを使って水やりを行う
const water = async () => {
var servo = obniz.wired("ServoMotor", {gnd:9, vcc:10, signal:11});
servo.angle(90.0);
servo.angle(0.0);
await obniz.wait(5000);
servo.angle(90.0);
}
// ########################################
// LINEBot イベント処理部分
// ########################################
const config = {
channelSecret: '*********',
channelAccessToken: '***********'
};
const line = require('@line/bot-sdk');
const client = new line.Client(config);
// ExpressからMessaging APIイベントを渡されて処理するところ
const handleEvent = async (event) => {
// テキストメッセージ以外を受信したときは何も行わずresolveを返す
if (event.type !== 'message' || event.message.type !== 'text') {
return Promise.resolve(null);
}
// テキストメッセージを受信したとき
//「湿度を教えて」と言われたとき
if (event.message.text === '湿度を教えて') {
// 待ってねというメッセージを「リプライ」で先に返す
await client.replyMessage(event.replyToken, {
type: 'text',
text: '調べております〜'
});
// obnizの湿度センサから値をとってくる(ブロッキング・時間のかかる処理で一旦ここで止まる)
const mois = await getObnizMois();
//湿度を計算する
let mois_par = Math.floor(mois/4.2*100);
// moisが取得できたら湿度によって湿度とメッセージをプッシュで送信する
if (mois < 1.47){
client.pushMessage(event.source.userId, {
type: 'text',
text: `湿度は${mois_par}%です。\n 土が乾いています。水をあげてください。`,
});
}else if(mois < 3.43){
client.pushMessage(event.source.userId, {
type: 'text',
text: `湿度は${mois_par}%です。\n 土はまだ湿っています。スクスク`,
});
}else{
client.pushMessage(event.source.userId, {
type: 'text',
text: `湿度は${mois_par}%です。\n 土はじゅうぶん湿っています。ニョキニョキ`,
});
}
//「水をあげて」とメッセージを送られたとき
} else if (event.message.text === '水をあげて'){
// 水をあげてきますね〜というメッセージを「リプライ」で先に返す
await client.replyMessage(event.replyToken, {
type: 'text',
text: '水をあげてきますね〜'
});
//水やり関数を実行する
await water();
client.pushMessage(event.source.userId, {
type: 'text',
text: `水をあげ終わりました!`,
});
}else{
// メッセージの中身が「温度教えて」以外だったとき
client.replyMessage(event.replyToken, {
type: 'text',
text: 'メニューから選択してね!'
});
}
// resolveを返す
return Promise.resolve(null);
}
// ########################################
// Expressサーバー部分
// ########################################
const express = require('express');
const PORT = process.env.PORT || 3000;
const app = express();
// 「(サーバーURL)/webhook」にアクセス(LINEサーバーからのWebhook)があったとき
app.post('/webhook', line.middleware(config), (req, res) => {
// 受信したイベントをターミナルに表示
console.log(req.body.events);
// イベントをhandleEventに渡して1つずつ処理
Promise.all(
req.body.events.map(handleEvent)
).then(
result => res.json(result)
);
});
// PORT番号のポートでサーバーを開始
app.listen(PORT);
console.log('express runnning: PORT =', PORT);
結果と今度の展望
- ガーデニング歴の長い友人に見せたところ、「これは欲しい」という感想をいただき、かなり実用的なものを製作できたのかと思います。
- 一方で、現状だと一回分の水やりしかできないので、今後は何回でも水やりができるようにポンプで組み上げる仕組みを導入したいです。