きっかけ
降水確率50%で洗濯物干すかどうか問題
今日は用事で朝から出かけなければならなりません。降水確率は50%で今は晴れています。あなたは洗濯物を外に干しますか?それとも室内に干しますか?
人類がこの問題に直面してから早五千年。いまだ答えが出ていない問題に休止符を打ちます。
雨が降りそうだったら自動で洗濯物を取り込んで濡れるのを防いでくれる。そんな夢のシステムを目指しました。
システム紹介
このシステムの名称は【洗濯物まもる君】で、指定した地域の天気予報を取得し雨が降りそうであればモータを動作させて洗濯物を雨から守るという仕組みです。また雨が降りそうなときはラインに通知を送信する機能も実装しました。
今回のシステムの動作の流れです。
- ESP32が起動すると指定されたWi-Fiにアクセス。
- Wi-Fiを通じてOpenWeatherMapのAPIにアクセスし,指定された地域の天気情報をjson形式で取得。
- 取得したjsonファイルを処理して雨に関係するデータだけを取り出し、分岐処理などを用いて雨が降りそうかどうかを判断する。
- 雨が降りそうであると判断された場合,LINE Notifyを利用して現在の天気とシステムの動作状況を送信する.また,アクチュエータ(今回の場合はサーボモータ)を動作させて,洗濯物を雨から守る。
今回は身近にあるサーボモータを使って、雨が降りそうになったらモータを回転させて糸を巻き取ることで洗濯物干しを雨が降りこまない位置まで移動させることにしました。
ハードウェア
今回のシステムで使用したハードウェアを以下に挙げます。
- ESP32 ESP32-WROOM-32D
- デジタルマイクロサーボモータ SG90
- 汎用ブレッドボード
- ジャンパ線
- 糸(洗濯物干しを引っ張っても切れないくらいの強度のもの)
サーボモータを回転させて糸を巻き取るだけなのでとても簡単です。
多くのブレッドボードは横に10個しか穴が無いので、今回はESP32の片側だけを刺すことにしました。(単なる確認不足です…)
-
まずはESP32をUSBが下に来るように持って、その右側をブレッドボードに刺します。
-
次にESP32の13,GND,5Vと書かれたピンからジャンパ線を出して、サーボモータのコネクタの「黄、茶、赤」に差し込んで接続します。
黄…信号線
茶…グランド(マイナス)
赤…電源(プラス)を表しています。また、信号線は白、グランドは黒で表されることも多いです。 -
サーボモータに糸を取り付けて、もう片方を洗濯物干しに結びつけます。
(本当は洗濯物干しを車輪で動かすなどした方がよいと思うのですが、いろいろあって糸で引っ張る方式となりました。)
ソフトウェア
今回は天気の情報を取得するために「OpenWeatherMap」という無料で利用できるAPIを使用しました。登録方法についてはこちらの方の記事がとても分かりやすかったので引用させていただきます。↓↓↓
また、ラインに動作の状況を通知するためにLINE Notifyと呼ばれるサービスを使用しました。こちらも登録が必要ですが簡単にできますので、分かりやすい記事を引用させていただきます。↓↓↓
では以下がソースコードです。
洗濯物まもる君ソースコード
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Servo.h>
const char* ssid = "接続するWi-FiのSSID";
const char* password = "Wi-Fiのパスワード";
Servo myservo; //Servoオブジェクトを作成
const String endpoint = "http://api.openweathermap.org/data/2.5/weather?q=kumamoto,jp&units=metric&APPID=";
const String key = "割り当てられたID";
char global_weather;
char goodweather;
double global_temp;
====
//APIが有効化されたかどうかをブラウザに入力して確認。q=tokyo,jpのところを変えると取得する都市を変更できる。
void sendLineNotify(String message) {
const char* host = "notify-api.line.me";
const char* token = "LINE Notifyに登録したときに表示されたトークン";
WiFiClientSecure client;
Serial.println("Try");
//LineのAPIサーバに接続
if (!client.connect(host, 443)) {
Serial.println("Connection failed");
return;
}
Serial.println("Connected");
//リクエストを送信
String query = String("message=") + String(message);
String request = String("") +
"POST /api/notify HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Authorization: Bearer " + token + "\r\n" +
"Content-Length: " + String(query.length()) + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n\r\n" +
query + "\r\n";
client.print(request);
//受信終了まで待つ
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
break;
}
}
String line = client.readStringUntil('\n');
Serial.println(line);
}
///////////////////////////////////////////////////////////////////////////////////
void setup() {
myservo.attach(13); //13番ピンにサーボ制御線(オレンジ)を接続
DynamicJsonBuffer jsonBuffer;//jsonを保存しておくバッファーを設定(Dynamicにすれば自動でサイズを指定してくれて便利)
String input ="{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
JsonObject& root = jsonBuffer.parseObject(input);
long time = root["time"];
Serial.println(time);
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
}
void loop() {
if ((WiFi.status() == WL_CONNECTED)) {
HTTPClient http;
http.begin(endpoint + key); //URLを指定
int httpCode = http.GET(); //GETリクエストを送信
if (httpCode > 0) { //返答がある場合
String payload = http.getString(); //返答(JSON形式)を取得
Serial.println(httpCode);
Serial.println(payload);
DynamicJsonBuffer jsonBuffer;
String json = payload;
JsonObject& weatherdata = jsonBuffer.parseObject(json);
//パースが成功したかどうかを確認
if(!weatherdata.success()){
Serial.println("parseObject() failed");
}
//各データを抜き出し
const char* weather = weatherdata["weather"][0]["description"].as<char*>();
const double temp = weatherdata["main"]["temp"].as<double>();
String message;
if(!strcmp(weather, "clear sky")){
message = "快晴だ!洗濯物がよく乾きそうだぜ!";
myservo.write(0); //洗濯物が乾く位置へ回転
}else if(!strcmp(weather, "few clouds")){
message = "晴れてる!\n洗濯物を守ります!!!!";
myservo.write(0); //洗濯物が乾く位置へ回転
}else if(!strcmp(weather, "scattered clouds")){
message = "曇ってる。雨は降らなさそう";
myservo.write(0); //洗濯物が乾く位置へ回転
}else if(!strcmp(weather, "broken clouds")){
message = "曇ってる。こりゃ一雨くるぞ\n洗濯物を守ります!!!!";
myservo.write(180); //洗濯物が濡れない位置へ回転
}else if(!strcmp(weather, "shower rain")){
message = "小雨が降ってる\n洗濯物を守ります!!!!";
myservo.write(180); //洗濯物が濡れない位置へ回転
}else if(!strcmp(weather, "rain")){
message = "本降りだぜ\n洗濯物を守ります!!!!";
myservo.write(180); //洗濯物が濡れない位置へ回転
}else if(!strcmp(weather, "thunderstorm")){
message = "感電しないように気をつけな!\n洗濯物を守ります!!!!";
myservo.write(180); //洗濯物が濡れない位置へ回転
}else if(!strcmp(weather, "snow")){
message = "雪だるま作ろう";
myservo.write(180); //洗濯物が濡れない位置へ回転
}else if(!strcmp(weather, "mist")){
message = "霧が出てる!気をつけな!";
myservo.write(180); //洗濯物が濡れない位置へ回転
}
Serial.println(message);
sendLineNotify(message);
//デバッグ用
Serial.print("weather:");
Serial.println(weather);
Serial.print("temperature:");
Serial.println(temp);
}
else {
Serial.println("Error on HTTP request");
}
http.end(); //リソースを開放
}
delay(60000); //60秒おきに更新
}
動作の様子
実際に動作している様子です。
最後に
今回ハードウェアに力を入れることができず、簡素なつくりになってしまいましたが、想定通りに動作させることができたので良かったです。
人類は技術によって天気に勝つことができました。
投稿者の人気記事
-
Shuntaroh
さんが
2021/02/28
に
編集
をしました。
(メッセージ: 初版)
-
Shuntaroh
さんが
2021/02/28
に
編集
をしました。
-
Shuntaroh
さんが
2021/04/28
に
編集
をしました。
ログインしてコメントを投稿する