#概要
出窓ガーデニング = 出窓菜園ティストのまえぷーです。
普段はnoteにマガジンとして掲載しておりますが、技術的なところの記事を書きたいと思いQiitaに投稿した次第です。
紆余曲折して給水システムの自動化と菜園管理のためにデータ収集システムを作りました。
この記事では、本システムの作り方を備忘録として書いています。
最後にソースコードを載せていますが、時間処理を適当に作っております。ちゃんと作ろうとしている方は時間処理以外をご参考にしていただければ幸いです。
できたのものはこちら。
【給水の様子】
自動給水システムの試運転。
— まえぷー@出窓菜園 BWG (@kmaepu) May 4, 2020
この後びしょ濡れになりました(笑) pic.twitter.com/EnmYwafViQ
#システム構成
本システムでは、土壌湿度センサを使用して土の乾き具合を計測し、乾いているとマイコンが判断したら給水ポンプの電源をONにして給水します。
と同時に、取得した土壌湿度データをAmbientでグラフ化しています。
#解説
##ポンプの駆動
ポンプの電源をリレーでON/OFF制御することにより、給水を制御しています。
リレーはこちらを使用しています。ポンプはUSB給電タイプのこちら
を使用しています。
なぜUSB給電にしたかたいうと、デモを持ち運ぶときにモバイルバッテリからの給電をしたかったためです。備え付けのシステムならば通常のコンセントプラグでも問題ないですね。
##土壌湿度の取得
土が乾いてきたかをセンシングするために、土壌湿度センサ**Gravity: Analog Soil Moisture Sensor For Arduino**を使用しました。
このセンサはアナログデータ(電圧)で土壌水分量が出力されます。 電源電圧5Vの時に出力電圧は0~4.2[V]のようです。サンプルコードから読み解くと、次のように乾き具合を3段階でわかるようです。(デジタル値)今回は300を閾値にして、以下になると給水を4秒間行うようにしました。
# 0 ~300 dry soil
# 300~700 humid soil
# 700~950 in water
デジタル値から土壌水分量に換算する式が理解できなかったので、今回は割愛しています。
よし。土場湿度をM5Stackで取得できるようになったぞ!#M5Stack pic.twitter.com/ctZhDJxZTH
— まえぷー@出窓菜園 BWG (@kmaepu) May 3, 2020
##データをアップロード
MQTTでいったんラズパイ上のNode-REDに送り、そこからAmbientへアップロードしています。
Node-REDのフローは次のようになっています。MQTTで受け取ったデータをjson形式に直してAmabientへ送信しています。
一見無駄な処理に見えますが、今後APIとつなげていこうと思うので拡張性を考えてこのようにしています。Node-REDのフローは次の通りです。受け取ったデータをamientノードで送っています。
##データ監視
Ambientでグラフ表示しています。特に難しい設定はしておらず、このようにグラフ表示できました。
今回はデータを見やすくするために、日にち単位で表示させています。
1日の様子を計測しましが、値のブレが大きいいのと、1日ではなかなか乾かないということがわかりました。1日というのも曇りだったので、晴天の直射日光が当たれば乾くスピードも速くなり、データに現れてくると思います。
#おわりに
実際に使ってみると、土壌湿度データが上限値に張り付いており、正しく計測できているか不安な状態です。ESP32のADCは癖があるようなので、外付けのADCで評価してみようと思いました。今回は補正も行っていないので、その辺も踏まえて取り組んでいった方がよいと分かりました。
それでも、乾いているか否かぐらいは判断できそうです。
※土壌水分量の計算ってもしかすると次の式であっている?
水分量[%] = デジタル値 / 4095 * 100
#ソースコード
#include <M5Stack.h>
#include <PubSubClient.h> // MQTT
#include <WiFi.h>
#include <WiFiClient.h>
#include <Wire.h>
#define RELAY 3
char *ssid = "*****"; // Wi-FiのSSID
char *password = "*****"; // Wi-Fiのパスワード
// MQTT 定義
const char *endpoint = "broker.shiftr.io";
const int port = 1883;
char *deviceID = "M5Stack"; // デバイスID
char *userName = "*******"; // MQTTブローカ認証ユーザ名
char *userPass = "*******"; // MQTTブローカ認証パスワード
char *pubTopic = "/pub/"; // メッセージを知らせるトピック
char *subTopic = "/sub/"; // メッセージを待つトピック
WiFiClient httpsClient;
PubSubClient mqttClient(httpsClient);
int data[2];
int avg_data = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
M5.begin();
pinMode(RELAY, OUTPUT);
digitalWrite(RELAY,LOW);
data[0] = analogRead(35);
// Wi-Fi設定
WiFi_init();
// connect MQTT
mqttClient.setServer(endpoint, port);
mqttClient.setCallback(mqttCallback);
connectMQTT();
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.setTextSize(3);
Serial.println("Start!");
}
void mqttLoop() {
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
}
long messageSentAt = 0;
int count = 0;
char pubMessage[128];
char f_moter= 0;
void loop() {
data[1] = analogRead(35); // new data
avg_data = (data[1] + data[0]) / 2;
Serial.print("Moisture Sensor Value:");
Serial.println(avg_data);
// 乾いてきたら給水
if(avg_data <= 300){
Serial.println("Moter ON");
digitalWrite(RELAY,HIGH);
delay(4000);
digitalWrite(RELAY,LOW);
Serial.println("Moter OFF");
f_moter = 1;
}else{
f_moter = 0;
}
data[0] = data[1];
// 5分間隔でデータアップロード
// MQTT 送信
sprintf(pubMessage, "{\"d1\": %d,\"d2\": %d}", avg_data, f_moter);
Serial.print("Publishing message to topic ");
Serial.println(pubTopic);
Serial.println(pubMessage);
mqttClient.publish(pubTopic, pubMessage);
mqttLoop();
delay(10000);
}
void WiFi_init(void){
// WiFi setting
Serial.println("Connecting to ");
Serial.print(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// WiFi Connected
Serial.println("\nWiFi Connected.");
M5.Lcd.setCursor(10, 40);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(3);
M5.Lcd.printf("WiFi Connected.");
}
void connectMQTT() {
mqttClient.setServer(endpoint, port);
mqttClient.setCallback(mqttCallback);
while (!mqttClient.connected()) {
if (mqttClient.connect(deviceID,userName,userPass)) {
Serial.println("MQTT Connected.");
int qos = 0;
mqttClient.subscribe(subTopic, qos);
Serial.println("Subscribed.");
} else {
Serial.print("Failed. Error state=");
Serial.print(mqttClient.state());
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void mqttCallback (char* topic, byte* payload, unsigned int length) {
String str = "";
static int pump[2] = {0,0};
Serial.print("Received. topic=");
Serial.println(topic);
}
``