Arduinoのライブラリで土壌センサーの情報を取得

組み込みエンジニア

こんにちは、ENGかぴです。

Arduino環境ではアナログ入力をAD変換する標準ライブラリが実装されています。土壌センサーの電圧出力をアナログピンに入力してAD変換値を読み込むことで土壌の水分量を確認することができます。

土壌センサーはSEN0114(DFROBOT製:秋月電子で購入)を使用しています。土壌センサーの動作検証とArduino UNOの拡張基板であるSD CARD SHIELDを使ってSDカードに土壌の状態を保存して動作確認を行いました。

Arduino UNO(以下Arduinoとします。)を対象とします。Arduinoのライブラリを使用して動作確認したことをまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

土壌センサーの情報を取得する

SEN0114は土壌の抵抗分で発生する電圧を出力するセンサーです。水分量が多いほど土壌の抵抗分が少なくなり電気を通しやすくなることを利用したものです。下記リンクにセンサーの情報とスケッチ例が説明されています。

SEN0114 Moisture Sensor-DFROBOT

Arduino用に出力調整されているためライブラリで読み取った値から土壌の状態を確認することができます。

センサー情報の使用例

  if( timdataget == TIME_UP){
    timdataget = TIM_MEAS;
    sen0114.buf[sen0114.wp] = analogRead(A0);

    if(++sen0114.wp >= MEAS_MAX){
      sen0114.wp = 0;
    }

    sum = 0;
    for(uint8_t i =0; i < MEAS_MAX; i++){
      sum += sen0114.buf[i];
    }

    sen0114.humid = sum >> 3; //8で割る
  }

タイマで100ms毎にSEN0114の値を読み込んでいますがアナログデータはノイズの影響などで実際の値から少し上下することがあります。そのため数回読み込んだデータを平均化して使用します。

例では100ms毎にデータを取得して最新のデータから8個分のデータを使用して平均値を算出しています。平均化する際に>>3で3回右にシフトしていますが8で割るのと同じ結果になります。

マイコンが計算する際にビットシフトで計算すると計算時間を減らすことができます。演算器を持たないマイコンを使用する場合においては効果的な方法です。

土壌センサーとしての用途を考えると短期間で土壌の水分がしないため計測の頻度を減らして平均化しない構成でも問題ないと思います。

動作検証

SEN0114の動作検証

SEN0114の計測値について動作確認を行いました。Arduinoから取得した値で土壌の状態の目安として0~300 dry soil、300~700 humid soil、700~950 in waterと記載されています。

SEN0114を2つのプローブ間を次のパターンを挿入して計測値の確認を行います。

  1. プローブ間を指で触れる
  2. プローブを水の中に入れる(上記の写真の通り)
  3. プローブ間をショートする

これらのパターンの結果をシリアルモニタで確認します。

SEN0114の動作確認(シリアルモニタ)

1. プローブ間を指で触れた場合は150程度の値となっています。2. プローブを水の中に入れた場合は750付近の値になっています。3. プローブ間をショートした場合は880付近の値になっています。

直接水につけた場合でも750程度の値になることが分かりました。プローブ間をショートすると880付近の値になりますが土壌に使用する場合ショートする可能性は限りなく低いことから750付近が最大値と考えて問題なさそうです。

750付近の値を取っていた場合、植物の根腐れやカビが生えてしまうなど別の原因になりそうです。個人的な主観になりますが半田こて用のスポンジを硬めに絞ったものを巻き付けても700付近の値になっていたため700で管理しても問題ないように感じました。

Arduinoの5Vはダイオード分だけ電圧が低下し4.84V程度になるためアナログ値から電圧値に変換する際は注意が必要です。例えばSEN0114のGNDとArduinoのGNDが共通としてSEN0114の電源を外部から5V印加した場合は値が高めに出てしまいます。

メーカの配線例を見ると電源をArduinoから5Vを取っているためAD変換値をそのまま読んでも問題ありません。

SDカードに履歴保存

void saveSd(void){

  if( timsave == TIME_UP ){
    timsave = TIM_SAVE;
    myfile = SD.open(filepath,FILE_WRITE);

    if( myfile ){ //ファイルが開けたら書き込む
      myfile.print("time:");
      myfile.print(timcnt);
      myfile.print(" humid:");
      myfile.println(sen0114.humid);
      myfile.close(); //ファイルを閉じる
    }
    ++timcnt;
  }
}

SDカードに土壌の水分量の履歴を保存するためタイマで管理しています。土壌は直射日光が当たらない限り乾燥しにくいため頻繁にタイムアップするような構成にする必要はありません。SDカードの操作については下記記事にまとめています。

ArduinoのライブラリでSDカードにデータを保存する

SDカードに履歴を保存することで観葉植物の土壌の様子が分かるため水やりの最適なタイミングを掴むことができます。面倒見が良すぎがあまり根腐れさせてしまうことに悩む可能性の軽減につながることが期待できます。

あなたのキャリアのお供に「生涯学習のユーキャン」

動作確認

動作確認用の回路図
動作確認用の回路図

SD CARD SHILDをArduino UNOに挿入して動作確認を行います。A0はCARD SHILD側のA0に配線していますがSD CARD SHILDはUNOのピンを延長しているだけなので配線上同じになります。

アイキャッチの観葉植物に水を与えてから計測を開始します。電源を入れると土壌の水分量の測定を開始し、1分毎に計測値をシリアルモニタ表示とSDカードに保存します。

動作確認の結果
動作確認の結果

30分程度放置して動作確認を行いました。シリアルモニタの表示と同様のデータがSDカードに保存されていることが確認できました。

土壌の水分量は短期間では乾かないことから計測頻度を落としてもよさそうです。最適な水やりのタイミングを考えるのは楽しいですが、いつも感覚で水をあげてしまう大雑把な私にはこのくらいの検証で十分だと感じました。

スポンサーリンク

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include <SPI.h>
#include <SD.h>

#define SD_CS 4
#define MEAS_MAX 8
#define TIM_MEAS 10
#define TIM_SAVE 6000
#define TIME_OFF -1 //タイマーを使用しない場合
#define TIME_UP 0 //タイムアップ
#define BASE_CNT 10 //ベースタイマカウント値

struct DIS_MEAN{
  uint8_t wp;
  uint32_t buf[MEAS_MAX];
  uint32_t humid;
};

uint32_t beforetimCnt = millis();
int16_t  timdataget;
int32_t  timsave;
String filepath = "sample.txt";
File myfile;
DIS_MEAN sen0114;
uint32_t timcnt;

void mainTimer(void);
void mainApp(void);
void saveSd(void);

void setup() {

  Serial.begin(115200);

  if(!SD.begin(SD_CS)){
    Serial.println("initialization failed!");
    while (1);
  }

  for( uint8_t i=0; i < 10; i++ ){
    timdataget = TIME_UP;
    mainApp();
    delay(100);
  }
}

void loop() {

  mainTimer();
  mainApp();
  saveSd();
}

void mainApp(void){
  uint16_t sum;

  if( timdataget == TIME_UP){
    timdataget = TIM_MEAS;
    sen0114.buf[sen0114.wp] = analogRead(A0);

    if(++sen0114.wp >= MEAS_MAX){
      sen0114.wp = 0;
    }

    sum = 0;
    for(uint8_t i =0; i < MEAS_MAX; i++){
      sum += sen0114.buf[i];
    }

    sen0114.humid = sum >> 3;
    //Serial.print("Moisture Sensor Value:");
    //Serial.println(sen0114.humid);
  }
}
/* SDカード管理 */
void saveSd(void){

  if( timsave == TIME_UP ){
    timsave = TIM_SAVE;

    myfile = SD.open(filepath,FILE_WRITE);

    if( myfile ){ //ファイルが開けたら書き込む
      myfile.print("time:");
      myfile.print(timcnt);
      myfile.print(" humid:");
      myfile.println(sen0114.humid);
      myfile.close(); //ファイルを閉じる

      Serial.print("time:");
      Serial.print(timcnt);
      Serial.print(" humid:");
      Serial.println(sen0114.humid);
    }
    ++timcnt;
  }
}
/* タイマ管理 */
void mainTimer(void){

  if ( millis() - beforetimCnt >= BASE_CNT ){
    beforetimCnt = millis();

    if( timdataget > TIME_UP ){
      --timdataget;
    }

    if( timsave > TIME_UP ){
      --timsave;
    }
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

あなたの市場価値を見いだす転職サイト【ミイダス】無料会員登録

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました