RobotShopのI2C 土壌水分センサー

RobotShopが扱う「Catnip I2C moisture sensor 土壌水分センサー」

を使ってM5Stack Toughとの組み合わせで、プランターの土壌水分をモニタリングする記事を見つけたので、センサーを購入してみた。
このセンサーはAmazonやAliexpressで売られている安価(¥500程度)な静電容量式土壌水分センサー

Capacitive Soil Moisture Sensor V1.2

では測定周波数が1.5MHzだが、本機は16MHzと比較的高く、温度や電気伝導度の影響も公開しているので補正をすればある程度実用になるのではないかと試してみた。
価格は¥2,234とやや高いが、本格的な静電容量式土壌水分センサーの10分の1以下である。

M5Stack用にはArduino用のライブラリがあり、Arduino IDEで開発できる。またインターフェースがI2Cであるため、アナログ出力の安価なセンサーより扱いやすい。
まずは、記事のM5Stack Toughに近いM5Stack Core2で試してみた。ケーブルにはGrove用の50cmコネクターケーブルを使って鉢花の土壌に挿してモニタリングしてみた。
すると比較的安定的に土壌湿度、照度、温度の値が出力され良好な感触を得た。

照度は最も暗い値が65,535で明るいほど低くなるので注意が必要だった。別の同シーリズ製品では防水のため黒い熱収縮チューブで覆っているために、照度の測定はできないものも存在する。しかし、せっかく値が出力されるのだから透明な熱収縮チューブでカバーしてみた。

実用的なロガーとして使用するため、タカチの防水ハンドヘルドケースWHシリーズを利用したソーラーパネル充電付きNi-MH電池駆動のシステムをWeb記事を参考に制作した。
M5Stack Core2は高価でフィールドロガーとしてはもったいないので、手持ちのM5StampC3を利用して組んでみた。

Arduino IDEプログラム


#include <Adafruit_NeoPixel.h> // LED用ライブラリー
#define LED_PIN 2
#include <Wire.h>
#include <I2CSoilMoistureSensor.h> // 土壌水分センサー用ライブラリー
#include "Ambient.h"
#include <WiFi.h>
#include <esp_task_wdt.h>
#define WDT_TIMEOUT 90 // WDTウォッチドッグタイマー時間を指定

I2CSoilMoistureSensor sensor;

// create a pixel strand with 1 pixel on PIN_NEOPIXEL
Adafruit_NeoPixel pixels(1, LED_PIN);

uint8_t color = 0, count = 0;
uint32_t colors[] = {pixels.Color(125, 0, 0), pixels.Color(0, 125, 0), pixels.Color(0, 0, 125), pixels.Color(125, 125, 125)};
const uint8_t COLORS_LEN = (uint8_t)(sizeof(colors) / sizeof(colors[0]));

// Slee time sec.
#define TIME_TO_SLEEP  60  //サンプリング間隔(秒)

// WiFi
#define SSID "xxxxxxxx"
#define PASSWORD "xxxxxxx"
#define RETRY 20

// Ambient
#define CHANNELID xxxxxxx
#define WRITEKEY "00000000000000"

WiFiClient client;
Ambient ambient;

bool connect_wifi() {
    int i = RETRY;
    WiFi.begin(SSID, PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
        if (!i--) {
            // リトライ回数の上限に達したら接続失敗
            Serial.println("Could not connect to WiFi");
            return false;
        }
        delay(500);
        color = 0; // LED Red
        pixels.setPixelColor(0, colors[color]);
        pixels.show();
        Serial.print(".");
    }

    Serial.print("WiFi connected\r\nIP address: ");
    Serial.println(WiFi.localIP());
    color = 1; // LED Green
    pixels.setPixelColor(0, colors[color]);
    pixels.show();
    return true;
}

void setup() {
    pixels.begin();  // initialize the pixel
    // Watch Dog Timerを設定
    esp_task_wdt_init(WDT_TIMEOUT, true);
    esp_task_wdt_add(NULL); // WDTの開始

    // Wireを指定する場合は本体のラベルを確認
    Wire.begin(9,8,10000UL);  // SDA,SCL,Clock 10KHz
    Serial.begin(115200);
   // シリアルモニタの準備ができるまで待つ
    while( !Serial ) {
    }
    Serial.println("Serial Port OK!");
    
    // センサーの初期化
    sensor.begin(); // reset sensor
    delay(1000); // give some time to boot up
    Serial.print("I2C Soil Moisture Sensor Address: ");
    Serial.println(sensor.getAddress(),HEX);
    Serial.print("Sensor Firmware version: ");
    Serial.println(sensor.getVersion(),HEX);
    Serial.println();

    // 測定
    while (sensor.isBusy()) delay(50); // available since FW 2.
    float mos = sensor.getCapacitance(); // 土壌水分値は190から460ぐらい
    float tmp = sensor.getTemperature()/(float)10; // 値は℃の10倍なので10分の1にする
    float lig = (sensor.getLight(true)-65535)*-1; // 最も暗い値が65535なので、差の逆数とする

    // 測定値出力
    Serial.printf("Soil mois: %4.0f\r\n", mos);
    Serial.printf("temp:%4.1f'C\r\n", tmp);
    Serial.printf("Light:%4.0f\r\n", lig);

    // 無線LANへ接続
    if (connect_wifi()) {
        ambient.begin(CHANNELID, WRITEKEY, &client);

        // 土壌湿度、温度、照度の値をAmbientに送信する
        ambient.set(1, mos);
        ambient.set(2, tmp);
        ambient.set(3, lig);

        if (ambient.send()) {
            Serial.println("The data was successfully sent to the ambient");
            color = 2; // LED blue
            pixels.setPixelColor(0, colors[color]);
            pixels.show();
            delay(1000);
        }else{
            Serial.println("Failed to send data to ambient");
        }
    }

    // ディープスリープモードに落ちる
    sensor.sleep(); // available since FW 2.3
    Serial.println("### DEEP SLEEP START");
    pixels.clear();
    pixels.show();
    esp_deep_sleep(TIME_TO_SLEEP * 1000000);
}

void loop() {
    // deepsleepでループすると毎回setup()が呼ばれるためloop()は特に何もしない
    esp_task_wdt_reset(); // 処理が正常に進んでいる場合にウォッチドッグタイマーを初期化
}

最初はM5Stack Core2と同じように測定値が得られたが、数回繰り返すと異常値になってしまった。

最初はセンサーに使っているケーブルを疑って、4芯シールド26AWGケーブル(MOGAMI 2948)50cmに交換してみたが変わらなかった。
次はGroveコネクター周りでM5Stack Core2とM5StampC3との大きな違いは電源部ではないかと考え、大きな違いはM5Stack Core2はバッテリーを内蔵しているため、センサーに流れる電圧の安定度が違うのではないかと考え、M5StampC3から引き出すGrove端子のセンサーへ供給する電源部を外部からの電源供給配線から直接繋いでセンサーへ供給してみた。すると今度は数回計測するとセンサーはフリーズ状態になりセンサーのLEDが点きっぱなしになってしまった。
電源にはNi-MHを4本使っているので、使い始めは5.2Vくらいになるので高すぎたのであろう。
ここで、土壌水分センサーの仕様を見返すと3.3〜5.0Vとなっている。電流も3.3Vの方が少ないので供給電圧を3.3VにするGrove端子を増設してみた。基板の上は標準のM5StampC3増設端子や電源から直に電源供給した端子などが並び美しくないが、なんとか収まった。


M5StampC3からGrove端子を実験のために3つ取り付けた。基板上のコネクタが3.3V電源。

結果は上々で、ベランダで育てているバラのプランターにセンサーを挿し込んでモニタリングをしている。

センサー、となりのチューブはスプリンクラー用

左の土壌水分値が急に上がっている箇所ははタイマー自動潅水による。右のLightはセンサー値から655,535を引いた逆数