2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

個人開発エンジニア応援 - 個人開発の成果や知見を共有しよう!-

【Arduino初心者向け】土壌水分量を測定してLCDにリアルタイム表示する(ESP32+SEN0193+LCD)

Last updated at Posted at 2023-09-18

:hammer:作るもの


土壌水分センサを使って、水分量を1秒ごとLCDに出力するモジュールを作ります!
また、スイッチで画面を切り替えてキャリブレーション(校正)できるようにします。
応用として、一定値を下回ったら自動でお水をやったり、通知する例が考えられます。


実際に作らずとも、Arduinoの使い方の一例として参考になれば嬉しいです!

:paperclip:材料・配線図

  1. マイコン (ESP32) ¥1,380
  2. 静電容量式土壌水分センサ (SEN0193) ¥1,056
  3. LCDモジュールシールド ¥999
  4. タクトスイッチ
  5. 抵抗 (10kΩ)
  6. コンデンサー (1μF 50V)
  7. ジャンパワイヤ
  8. ブレッドボード

pic_SoilMoistureArticle.png

1. マイコン (ESP32)

本記事のプログラムはインターネット通信をしていませんが、Wi-Fiモジュールを搭載しているESP-WROOM-32を使っているため、インターネット通信が可能です。

2. 静電容量式土壌水分センサ (SEN0193)

より安価な電気抵抗式の土壌水分センサでも良いのですが、今回は耐久性・精度が優れた静電容量式のセンサを使いました。
自動水やり機のように土に常に挿す場合は、耐久性のある静電容量式の方が適しています。

マイコン 土壌水分センサ
3.3V Vcc
GND GND
GPIO26 アナログ出力電圧

参考
製造元の解説記事
静電容量型の土壌湿度センサを使ってArduinoで土の水分量測定

3. LCDモジュール

モジュールシールドが付いており、はんだ付けが不要なものを購入しました。
LCDの裏はこのようになっています。つまみを回すことで、画面の明るさを調整できます。

pic_LCD.png

マイコン LCDモジュール
5V Vcc
GND GND
SDA SDA (GPIO21)
SCL SCL (GPIO22)

参考
ArduinoでLCDに文字表示

4. タクトスイッチ

キャリブレーションのモード切り替えのために使いました。
今回はマイコンに対してのみON/OFFを入力するため、上下の端子とも同じ配線にしています。
タクトスイッチの原理については、下記サイトさんの解説がとてもわかりやすいです。

参考
Arduino(アルディーノ)電子工作の基本⑥ スイッチの状態を読み取る

5. 抵抗 (10kΩ)

抵抗は、マイコン <-> タクトスイッチ 接続部のプルアップのために使いました。
プルアップ抵抗とは、電源やGNDとモジュール間の接続がない状況を避け、動作を安定させるために追加します。
詳しい原理については、下記サイトさんの解説がとてもわかりやすいです。

参考
プルアップ抵抗・プルダウン抵抗とは?電子回路に必須の考え方

6. コンデンサー (1μF 50V)

コンデンサーは、ESP32へ安定してスケッチを書き込むために使いました。
ESP32にそのままスケッチを書き込もうとした際、以下のように、ESP32とPCが正常に通信できなかったと怒られました_:(´ཀ`」 ∠):

A fatal error occurred: Timed out waiting for packet header

そこで、EN <-> GND へ0.1μF~2.2μF程度のコンデンサを追加することで、正常に書き込めるようになりました。
コンデンサーを追加してもエラーが解決しない場合は、下記サイトさんをご参照ください。

参考
書込エラー対応「A fatal error occurred: xxxxx」

:pen_fountain:プログラム

土壌水分量測定プログラム
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

#define SEN0193_PIN 26
#define SWITCH_PIN 27

LiquidCrystal_I2C lcd(0x27,16,4);

int ADC_AIR = 0; // 空気中の実測値
int ADC_WET = 0; // 水に浸した実測値

int mode = 0;    // モード切り替え用

/* センサの測定値を水分量[%]に変換する関数 */
int getMoisture(int val) {
    if (val > ADC_AIR) {
        val = ADC_AIR;
    }
    else if (val < ADC_WET) {
        val = ADC_WET;
    }

    int mois = 100 - map(val, ADC_WET, ADC_AIR, 0, 100);
    return mois;
}

/* ADC_AIR,ADC_WATにEEPROMの値を格納する関数 */
void getEEPROM() {
    EEPROM.get(0, ADC_AIR);
    EEPROM.get(4, ADC_WET);
}

/* setup関数 */
void setup(void) {
    Serial.begin(115200);

    pinMode(SEN0193_PIN, INPUT);
    pinMode(SWITCH_PIN, INPUT);

    lcd.init(); 
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Ready...");

    EEPROM.begin(4*2); // int=4byte * 2vals
    delay(1000);
    getEEPROM();

    delay(3000);
}

/* モードを変更する関数 */
void changeMode() {
    if(mode == 0)      { mode = 1; }
    else if(mode == 1) { mode = 2; }
    else if(mode == 2) { mode = 3; }
    else if(mode == 3) { mode = 0; }
}

/* loop関数 */
void loop(void) {
    int vol = analogRead(SEN0193_PIN);
    int moisture = getMoisture(vol);

    /* ボタンが押されたらモード切り替え */
    if(digitalRead(SWITCH_PIN) == LOW) {
        changeMode();
    }

    switch(mode) {
        /* mode0: 毎秒測定して水分量を表示する */
        case 0:
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("MODE0: Measure");
            lcd.setCursor(0, 1);
            lcd.print("Vol : " + String(vol));
            lcd.setCursor(0, 2);
            lcd.print("Mois: " + String(moisture) + " %");
            lcd.setCursor(0, 3);
            lcd.print("Set : A=" + String(ADC_AIR) + " W=" + String(ADC_WET));
            
            Serial.println("Vol: " + String(vol) + "  Mois: " + String(moisture) + " %");

            break;
        
        /* mode1: 水分量0%をキャリブレーションする */
        case 1:
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("MODE1: Calib AIR");
            lcd.setCursor(0, 1);
            lcd.print("Vol : " + String(vol));
            ADC_AIR = vol;

            break;
        
        /* mode2: 水分量100%をキャリブレーションする */
        case 2:
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("MODE2: Calib WET");
            lcd.setCursor(0, 1);
            lcd.print("Vol : " + String(vol));
            ADC_WET = vol;
            
            break;

        /* mode3: キャリブレーションした値をEEPROMに保存する */
        case 3:
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("MODE3: Saved ^-^");
            lcd.setCursor(0, 3);
            lcd.print("Set : A=" + String(ADC_AIR) + " W=" + String(ADC_WET));

            EEPROM.put(0, ADC_AIR);
            EEPROM.put(4, ADC_WET);
            EEPROM.commit();  // EEPROMへ上書き

            delay(1000);

            changeMode();  // mode0に切り替え

            break;
    }

    delay(1000);
}

4つのモード

プログラムには以下の4つのモードがあり、スイッチが押されると次のモードに推移します。(正確には、loop関数が呼び出されるタイミングで押されていたら推移します)

Mode 実行内容
0 水分量を測定するモード。
LCDの2行目に測定値を、3行目に水分量の計算結果を、4行目に現在設定されている0%,100%時の測定値を表示する。
1 0%時の測定値をキャリブレーションするモード。
LCDの2行目に現在の測定値を表示し、スイッチが押されたタイミングの測定値を0%として保存する。
2 100%時の測定値をキャリブレーションするモード。
LCDの2行目に現在の測定値を表示し、スイッチが押されたタイミングの測定値を100%として保存する
3 測定結果をEEPROMに保存するモード。
保存後はMode0に自動で戻る。

水分量の計算

水分量の計算 getMoisture() は、空気中での測定値を最小値0%、水中での測定値を最大値100%と仮定して、簡易的に線形マッピングしているだけです。
実際の土を使う場合、乾燥した状態で空気中の測定値を、十分湿らせた状態で水中の測定値をキャリブレーションすることで、より細かく水分量を算出できると思います。

EEPROM (不揮発性メモリ)

このプログラムでは、キャリブレーションした0%,100%の測定値をもとに水分量を計算していますが、これらの値が電源を切って消えてしまうと、再度キャリブレーションしなくてはなりません。
それは非常に面倒なので、電源を切っても値を保持しておけるEEPROM (不揮発性メモリ) を使用しています。
ここに格納することで、再起動しても直近のキャリブレーション時の測定値をもとに水分量を測定できます。

ただし、EEPROMの物理的な構造上書き込み回数に制限があります。
(といっても10万回くらいはいけるようです)
そのため、このプログラムでは、setup()で1回、loop()でMode3になった時のみ1回使用するようにしています。
このような物理的な制約があるのも、電子工作の面白いところです(´・ω・`)

参考
ArduinoのEEPROMを使う

:sparkles:終わりに

私は農業のIoT化をテーマに共同研究をしていて、その遊びがてらこのモジュールを作成しました。今回はインターネット通信をしていないのでIoTではないですが…笑
水分量(体積含水率)の正確な測定には、塩害やセンサの寿命を考慮する必要があり、土壌水分センサ1つ+今回のような簡易的なキャリブレーションでは不十分です。
現在は畑での灌水モジュールを作成中で、論文サーベイや議論をする毎日です。農学系の方と話すと、自分と全く視点が違ってとても勉強になります。

ありがとうございました>ω</

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?