見出し画像

デジタル温湿度センサーを使ってみる

jun-fu@bitengineers です。

前回 note に書いた firmware を使って計測を進めているのですが、土中センサーの銅箔部分が腐食してしまいデータがうまく取れなくなってしまいました。

分かっていた結果ですが、誤算としては数週間という短期間で起こってしまったことです。半年位は保つのではないか?と思ってましたが。。。腐食が酷いものだとパッドが無くなってます。こんなになるんですね。

多めに購入しちゃいました。。。orz

画像1

M5Stack の Earth Moisture Sensor Unit はここから先は使い物にならないので別のセンサーを使います。静電容量式の土壌センサーも選択肢としてあるのですが、敢えて違う道を模索したいと思います。

画像2

前からちょっと気になっていた IC 搭載でセンサープローブ部分がステンレスで被われており、防水(IP67) とあるので腐食に対しては対策されています。このセンサーを使って土中水分を測ってみることにします。

SHT3x デジタル温度・湿度センサー

サイズ:	2.5 x 2.5 x 0.9 mm
出力:	デジタルインターフェース (I²C)、 Voltage Out
電源電圧:	2.15 ~ 5.5 V
消費電力:	4.8 µW (最小再現性、毎秒1測定時)
動作湿度範囲:	0 ~ 100% 相対湿度
動作温度範囲:	-40 ~ +125°C (-40 ~ +257°F)
湿度応答時間:	8 s (tau 63%)

datasheet はこちら

SHT30 が内部で使用されています。消費電力も低いので問題なく使えそうです。接続は I2C で行います。5V, GND, SCK, SDA の 4 本を半田付けして各線が接触しないように被ってから外側を防水目的でテープで巻いてます。

画像4

画像5

このICは内部でセンサー値を AD 変換後補正、校正した後のデータをデジタル線に乗せているので、サンプリングレートや補正処理などを考慮しないとならないようなアナログセンサーと比較して圧倒的に導入が容易です。 

画像5

I2C で提供されている計測方法には単発測定モード、周期的連続測定モードがあります。データの取得頻度や精度に合わせてコマンドが使い分けられるようになってます。

センサー値の読み取り

実際に接続してみます。I2C なので M5Stack デバイスの PortA に繋げばすぐに使えます。 

I2C SDA (data pin), SCK(clock pin) を 32, 33 として Clock を 400k で設定した例が以下。

#define SHT30_I2C      I2C_NUM_1
#define SHT30_I2C_ADDR 0x44
#define SHT30_I2C_SDA  32
#define SHT30_I2C_SCK  33
#define SHT30_I2C_CLK  400 * 1000

esp_err_t sht30_init(void)
{
 esp_err_t err;
 i2c_config_t i2c_config = {
   .mode = I2C_MODE_MASTER,
   .sda_io_num = SHT30_I2C_SDA,
   .scl_io_num = SHT30_I2C_SCK,
   .sda_pullup_en = GPIO_PULLUP_ENABLE,
   .scl_pullup_en = GPIO_PULLUP_ENABLE,
   .master.clk_speed = SHT30_I2C_CLK,
 };
 err = i2c_param_config(SHT30_I2C, &i2c_config);
 if (err != ESP_OK) {
   return err;
 }
 err = i2c_driver_install(SHT30_I2C, I2C_MODE_MASTER, 0, 0, 0);
 if (err != ESP_OK) {
   return err;
 }
 i2c_set_timeout(SHT30_I2C, 400000);
 return ESP_OK;
}

次に、使用に最適なコマンドを選択します。

画像7

周期的連続測定モード(2[mps], 高[繰り返し精度])を選択しました。I2C の通信仕様に合わせてコマンドを送ります。

画像8

図のように記述されています。

これはSTART ビットでI2C通信を開始し I2C アドレス+Write を選択、master 書き込みモードとなり、Ackを受け取る。コマンドをMSB, LSB の順に送りAck を受け取る。そのままSTART ビットで I2Cアドレス+Read で受信を指定する。温度2byte が MSB, LSB で流れてきて CRC も続く。湿度も同じ。最後は Ack でなく Nack で 通信をSTOPする。これをコード化します。

esp_err_t sht30_read_measured_values(uint16_t *temperature, uint16_t *humidity)
{
 esp_err_t err = ESP_OK;
 uint8_t code[] = { 0x22, 0x36 };
 uint8_t crc[2];
 uint8_t temp[2];
 uint8_t hum[2];
 i2c_cmd_handle_t cmd = i2c_cmd_link_create();
 i2c_master_start(cmd);
 i2c_master_write_byte(cmd, (SHT30_I2C_ADDR<<1)|I2C_MASTER_WRITE, true);
 i2c_master_write_byte(cmd, code[0], true);
 i2c_master_write_byte(cmd, code[1], true);
 i2c_master_start(cmd);
 i2c_master_write_byte(cmd, (SHT30_I2C_ADDR<<1)|I2C_MASTER_READ, true);
 i2c_master_read_byte(cmd, temp, I2C_MASTER_ACK);
 i2c_master_read_byte(cmd, temp+1, I2C_MASTER_ACK);
 i2c_master_read_byte(cmd, crc, I2C_MASTER_ACK);
 i2c_master_read_byte(cmd, hum, I2C_MASTER_ACK);
 i2c_master_read_byte(cmd, hum+1, I2C_MASTER_ACK);
 i2c_master_read_byte(cmd, crc+1, I2C_MASTER_NACK);
 i2c_master_stop(cmd);
 err = i2c_master_cmd_begin(SHT30_I2C, cmd, pdMS_TO_TICKS(3000));
 i2c_cmd_link_delete(cmd);

 ESP_LOGI("sht30", "temp %d(%x, %x), crc %d(%x), check result = %d", (uint8_t)((temp[0]<<8)+temp[1]), temp[0], temp[1], crc[0], crc[0], sht30_check_crc(temp, crc[0]));
 
 ESP_LOGI("sht30", "humdity %d(%x, %x), crc %d(%x), check result = %d", (uint8_t)((hum[0]<<8)+hum[1]), hum[0], hum[1], crc[1], crc[1], sht30_check_crc(hum, crc[1]));
 *temperature = (temp[0] << 8) | temp[1];
 *humidity = (hum[0] << 8) | hum[1];
 return err;
}

i2c_cmd_handle_t 型に通信仕様を書き込んで i2c_master_cmd_begin 関数で実際に通信を行います。

データはこれで取得できます。このICではCRCで取得した値の正当性を確認できます。CRC 仕様は

画像6

以上の CRC-8 の諸々のパラメータをコード化すると以下のようになります。 uint8_t *buf に2byte 温度や湿度が入ってくることを想定しています。

#define SHT30_CRC_LEN 2
#define SHT30_CRC_POLYNOMIAL 0x31

static uint8_t sht30_check_crc(uint8_t *buf, uint8_t crc)
{
 uint8_t v = 0xff;
 for (int i = 0; i < SHT30_CRC_LEN; i++) {
   v ^= *buf++;
   for (int j = 0; j < 8; j++) {
     if (v & 0x80) {
       v <<= 1;
       v ^= SHT30_CRC_POLYNOMIAL;
     } else {
       v <<= 1;
     }
   }
 }
 return v; // ^ 0x00;
}

CRC で確認しないと痛い目に会うのでしっかりチェックしましょう(実際に会いましたw)。

先ほど選択した周期的連続測定コマンドは取得頻度が大きくずれるとどうも値がうまく取れない時があるようです。なので上記コードは実行頻度にも影響されます。

温度も湿度も取得した値はただの数値であり変換する方程式が指定されています。

温度がセルシウス℃変換で

画像13

湿度が相対湿度と呼ばれるもので以下です。

画像12

接続については以上です。


土中水分をどう測るか?

センサーに加工して Groveコネクタを付け、アプリケーションを用意したのでようやくやりたいことができるようになりました。が、どうやったら測れるかまだ確立できてません。

実は以前から土中にこのセンサーを埋めて計測をしていたのですが思ったようなデータが取れませんでした。

画像11

やる気のないグラフですがw

腐食に負けてしまった Earth Moisture Sensor Unit (青) と今回の SHT30 (緑) で取り貯めたデータを日付別に描画してみました。(青い線は徐々に腐食されているので信用できないデータです。)

一つの鉢を二つのセンサーで測ったものですが、まったく相関しているように見えないです。この原因を解明しないとならないようです。

計測器を作ってみた

今回の原因と思われる可能性について、

まず計測時の CRC チェックが甘い箇所があり、正しい値が取れていない場合に -45℃、100RH%が取れてしまうという痛恨のミスがあったこと。 これが実際に起こっていた可能性。CRC チェック大事です。 <= これを 1 とします。

次に IC のアプリケーションノートを開いてみましたが、ある程度空気の循環がある場所での計測が好ましいとありました。一度鉢に水を入れたタイミングからセンサーの周辺は空気の循環がされず、実際に湿度が100RH%だった可能性。 <= これを 2 とします。

1 については修正したものを用いて再度挑戦します。もう一個埋めてみる、も何か分かるかもしれないですね。

2 については今回採用してみた SHT30 についてもう少し理解を深める必要がありそうです。計測方法を変えるなど。土中ではなく別の場所で測ってみたり、鉢の足の間を測ったり、土の表面を測ったり...色々試してみたいと思います。

この用途のために簡単に使用でき、どういう値が取れるのかがすぐ把握できるように Display に計測値を表示して UI をデザインしてみました。

画像9

どこを計測したらどういう値がとれるのか、調べるのには便利そうです。

画像14

画像15

この辺りをもう少し研究して、続きはまた何か書いてみます。

終わりに

今回の SHT30 ですが、 ENVⅢ にも同じものが使われています。 ENVⅢを Port A に挿せばそのまま同じように動作します。

画像16

無駄にカラーバリエーションも作って遊んでみましたw

github に置いてあります。

今回は以上です。

盆栽要素薄め.

この記事が気に入ったらサポートをしてみませんか?