LoginSignup
43
34

More than 3 years have passed since last update.

出窓菜園の自動給水と、土壌湿度データの可視化システムを作った

Last updated at Posted at 2020-05-06

概要

 出窓ガーデニング = 出窓菜園ティストのまえぷーです。

 普段はnoteにマガジンとして掲載しておりますが、技術的なところの記事を書きたいと思いQiitaに投稿した次第です。

 紆余曲折して給水システムの自動化と菜園管理のためにデータ収集システムを作りました。
この記事では、本システムの作り方を備忘録として書いています。
 最後にソースコードを載せていますが、時間処理を適当に作っております。ちゃんと作ろうとしている方は時間処理以外をご参考にしていただければ幸いです。

 できたのものはこちら。
【給水の様子】

【データの可視化】
ambient.JPG

システム構成

 本システムでは、土壌湿度センサを使用して土の乾き具合を計測し、乾いているとマイコンが判断したら給水ポンプの電源をONにして給水します。
 と同時に、取得した土壌湿度データをAmbientでグラフ化しています。

出窓菜園 自動給水システム.jpg

解説

 

ポンプの駆動

 ポンプの電源をリレーでON/OFF制御することにより、給水を制御しています。

 リレーはこちらを使用しています。ポンプはUSB給電タイプのこちら
を使用しています。

 なぜUSB給電にしたかたいうと、デモを持ち運ぶときにモバイルバッテリからの給電をしたかったためです。備え付けのシステムならば通常のコンセントプラグでも問題ないですね。

 USB給電用の治具を次のように製作しました。
image.png

土壌湿度の取得

 土が乾いてきたかをセンシングするために、土壌湿度センサ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

 デジタル値から土壌水分量に換算する式が理解できなかったので、今回は割愛しています。

データをアップロード

 MQTTでいったんラズパイ上のNode-REDに送り、そこからAmbientへアップロードしています。

20200504_102902.jpg

 Node-REDのフローは次のようになっています。MQTTで受け取ったデータをjson形式に直してAmabientへ送信しています。

 一見無駄な処理に見えますが、今後APIとつなげていこうと思うので拡張性を考えてこのようにしています。Node-REDのフローは次の通りです。受け取ったデータをamientノードで送っています。
image.png

データ監視

 Ambientでグラフ表示しています。特に難しい設定はしておらず、このようにグラフ表示できました。
今回はデータを見やすくするために、日にち単位で表示させています。

image.png

 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);

}
``
43
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
34