はじめてのMKR ZERO (10) ディジタル温湿度センサSHT35を利用

 前回、温度を測るためにアナログ出力のLM35DZを利用しました。ここでは、ディジタル出力のなかで信号線2本で接続ができるI2CインターフェースのSHT35を利用します。I2Cはアイ・スクエアード・シーと呼ぶのが一般的です。
 そして、Groveコネクタで接続します。Groveコネクタは信号線を二つ、電源の計4本を接続するのにはもってこいのピン数で、かつ、抜き挿しがしやすく、実験にはぴったりです。

最初は動作確認

 電子部品を購入したら、動作確認をします。100%正常なことが多いのですが、前回使ったLM35DZをアマゾンで購入したときは、10個全部が不良品でした。いわゆるバチモンも出回っています。今回はSeeedの製品をスイッチサイエンスで購入しました。

 まず、ライブラリを導入して、温度と湿度を表示することを確認します。ライブラリは、Arduino IDEのライブラリ管理から行える場合が多いのですが、新しい製品などは、GitHubもしくはメーカのWebサイトに置いてあることがあるので、ZIPファイルをダウンロードしてインストールします。
 先ほどのスイッチサイエンスの販売ページからSeeed_SHT35-master.zipをダウンロードします。ファイルはダウンロード・フォルダに入っています。
 メイン・メニューのスケッチから->ライブラリをインクルード->.ZIP形式のライブラリをインクルードを選びます。

 選ぶのは、ダウンロード・フォルダにあるSeeed_SHT35-master.zipです。

 読み込むと、黒いエリアの上のステータス・エリアに、ライブラリが追加されたというメッセージが出ます。ZIPファイルにソースが入っていないときなどは、別のメッセージが表示されます。

 ツール->ライブラリを管理からライブラリマネージャを起動し、SHT-35を検索欄に入れて、ライブラリがインストールされていることを確認します。

 ライブラリと同時にサンプルが入っているので、スケッチ例から探します。

 basic_demoを読み込んで、コンパイル、実行します。温度と湿度を表示しました。

 確認のために、I2CScannerを読み込み、コンパイル、実行します。0x45がスレーブ・アドレスのようです。

SHT-35のおもなスペック

 いままでSHT-31を利用してきました。温湿度センサを比較します。

型名 相対湿度の確度 センサの材質 参考価格[円] そのほかの測定項目 温度の確度 インターフェース 電源電圧[V]
 DHT11  ±5%RH  ポリマ  300  温度  ±2℃  独自シリアル  3.3~5.5
 AM2322  ±2%RH  ポリマ   700  温度  ±2℃  I2C  3.1~5.5
 SHT-35  ±1.5%RH  CMOS?  1700  温度  ±0.1°C  I2C  2.4~5.5
 SHT-31  ±2%RH  CMOS?  950  温度  ±0.2°C  I2C  2.4~5.5
 SHT-11  ±3.5%RH  ポリマ  4300  温度  ±0.5°C  2線  2.4~5.5
 BME280  ±3%RH  ?  1080 温度、気圧  ±0.5°C I2C、SPI  1.71~3.6

 SHT-35は、一連の製品の中では一番性能が良いようです。SHT-35のデータシートはSHT31/30と同じなので、性能以外は同じに扱えます。

I2CはWireライブラリを利用する

 I2Cでは、マスタが一つもしくは複数のスレーブと通信します。ここでマスタはArduino MKRZEROです。今回のスレーブ・デバイスはSHT-35です。複数のスレーブがつながっているときは、スレーブ・アドレスでマスタが個々を判別します。そのため、上記のようにI2CScannerというスケッチで、つながっているスレーブ・デバイスを確認しています。

 I2Cで使われる信号はSDASCLの二つです。SDAはデータ線で、マスタがスレーブに対して、もしくはスレーブからマスタに向けてという双方向のデータが流れます。SCLはマスタが出すクロックです。この周波数はデフォルトで100kHz、少し速いのであれば400kHzが使われます。SHT-35はその上の1MHzまで対応しています。

 通信は非同期で、スタート・コンディションから始まり、コマンドやデータのやり取りが終わると、ストップ・コンディションで終了します。デバイスによっては、スタート・コンディションで始まり、リピーテッド・スタート・コンディション、最後はストップ・コンディションという形態もまれにあります。

 スタート・コンディションは、WireライブラリのWire.beginTransmission(SHT35_address);です。ストップ・コンディションはWire.endTransmission();です。このやり取りの間で、コマンドをマスタがスレーブに送信するのは、Wire.write(byte);で1バイトを送ります。2バイトであれば、続けてWire.write(byte);を記述します。

 送るbyteは、スレーブ・デバイスのデータシートに書かれています。多くのデバイスでは、消費電流を抑えるために、コマンドを送ると1回だけデータを用意してくれるモードと、一度コマンドを送ると電源を切るまで連続で最新データを用意してくれるモードの二つがあります。電子工作では、連続モードを選ぶのが無難です。
 また、コマンドを送っても、データを用意するのに時間がかかる場合があります。その場合、SCLをLOWのまま維持し、データが用意できたら普通のクロックに復帰するクロック・ストレッチをサポートしているデバイスや、DataReadyピンを別に用意していて、それを監視してねというデバイスがあります。

 仮に、連続モードの設定ができたとします。これは一度実行すればよいので、setup()内に記述します。

 loop()内で、データを読み取ります。Wire.requestFrom(SHT35_address, 6); では6バイトのデータを読み取ります。

_1stByte = wire.read();
_2ndByte = wire.read(); 
_3rdByte = wire.read();
_4thByte = wire.read();
_5thByte = wire.read();
_6thByte = wire.read();

 これで6バイトが読み取れました。スタート・コンディションとストップ・コンディションはWire.requestFrom()が面倒を見てくれます。6バイト読み取ったら、ストップ・コンディションになるので、Wire.endTransmission();は不要です。

SHT-35のスケッチ

 こちらのSHT-31のスケッチをそのまま持ってきました。

2行目 SeeedのSHT-35ボードはスレーブ・アドレスが0x45に設定されているので、0x44から変更しました。定数なので、constをつけるべきかもしれません。もちろん、#define SHT31_address 0x45 でもOKです。

10行目 Wire.write(0x23);Wire.write(0x34); 周期的連続測定コマンド (測定頻度;4回/秒、繰り返し精度レベルは「高」)という設定条件をスレーブのSHT-35へ送ります。

19行~24行 SHT-35が用意した6バイトを配列に読み込みます。最初の2バイトは温度、3バイト目はそのエラー・チェックのCRC、4、5バイトは湿度、最後は湿度のCRCデータです。読み出したデータが、バスを伝わる途中でノイズなどによって正しくないデータを受け取ったかを確認するのがCRCなので、電子工作的には無視してもかまいません。
 温度と湿度のデータは、16ビットで構成されていて、最初のバイトが上位バイト、次が下位バイトです。温度であれば、Stemp = readbuffer[0] << 8 ;で8ビット上位にシフトして、次のバイトを読んで16ビットに合成します。Stemp = Stemp + readbuffer[1];

31行目 temp = -45 + Stemp * 175.0 / 65535.0;データシートに書かれている変換式をそのまま記述しています。摂氏の単位の温度が求まります。

49行目以降 crc8()とreflect()関数はCRC-8の計算をしています。

#include <Wire.h>
unsigned char SHT35_address = 0x45;
int Stemp;int SRH;
float temp;float RH;
byte readbuffer[6];

void setup() {
Wire.begin();
Wire.beginTransmission(SHT35_address);
Wire.write(0x23);Wire.write(0x34);
Wire.endTransmission();
Serial.begin(9600);
delay(10);
Serial.println("start ");
}

void loop() {
Wire.requestFrom(SHT35_address, 6);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
readbuffer[2] = Wire.read(); // CRC
readbuffer[3] = Wire.read();
readbuffer[4] = Wire.read();
readbuffer[5] = Wire.read(); // CRC
// Serial.println(readbuffer[0]);Serial.println(readbuffer[1]);Serial.println(readbuffer[2]);Serial.println(readbuffer[3]);Serial.println(readbuffer[4]);Serial.println(readbuffer[5]);
if (readbuffer[2] == crc8(readbuffer)) {
Serial.println( "CRC-temp "+String(readbuffer[2])+" CRC "+String(crc8(readbuffer)));
Stemp = readbuffer[0] << 8 ;
Stemp = Stemp + readbuffer[1];
// Serial.println(String(Stemp,HEX)+" ");Serial.println(Stemp);
temp = -45 + Stemp * 175.0 / 65535.0;
}
if (readbuffer[5] == crc8(readbuffer+3)) {
Serial.println( "CRC-RH "+String(readbuffer[5])+" CRC "+String(crc8(readbuffer+3)));
SRH = readbuffer[3] << 8 ;
SRH = SRH + readbuffer[4];
// Serial.println(String(SRH,HEX)+" ");Serial.println(SRH);
RH = 100.0 * SRH / 65535.0;
}
Serial.println(" temp= "+String(temp,1)+"'C");
Serial.println(" RH%= "+String(RH,0));
delay(5000);
}

unsigned char table[256] ={
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

unsigned char crc8(const unsigned char * data){
unsigned char crc = 0xff;
crc = table[reflect(data[0]) ^ crc];
crc = table[reflect(data[1]) ^ crc];
return reflect(crc);
}

unsigned char reflect(unsigned char c){
unsigned char r=0;
for (int i=0;i<8;i++){
if (c&(1<<i)){
r=r|(1<<(7-i));
}
}
return r;
}

 (※)ビットの逆順は、こちらのプログラムを利用させていただきました。

単発測定

  単発測定コマンド0x2400(繰り返し精度レベル;高、クロック・ストレッチは無効)を実行して読み取るように変更しました。1回だけの変換を要求するので、loop()の中で毎回そのコマンドを送っています。

#include <Wire.h>
unsigned char SHT35_address = 0x45;
int Stemp;int SRH;
float temp;float RH;
byte readbuffer[6];

void setup() {
Wire.begin();
Serial.begin(9600);
delay(10);
Serial.println("start ");
}

void loop() {
Wire.beginTransmission(SHT35_address);
Wire.write(0x24);Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(SHT35_address, 6);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
readbuffer[2] = Wire.read(); // CRC
readbuffer[3] = Wire.read();
readbuffer[4] = Wire.read();
readbuffer[5] = Wire.read(); // CRC
Serial.println( "read CRC-temp "+String(readbuffer[2])+" "+String(crc8(readbuffer))+" <-calculatedCRC ");
if (readbuffer[2] == crc8(readbuffer)) {
Stemp = readbuffer[0] << 8 ;
Stemp = Stemp + readbuffer[1];
temp = -45 + Stemp * 175.0 / 65535.0;
}
Serial.println( "read CRC-RH "+String(readbuffer[5])+" "+String(crc8(readbuffer+3))+" <-calculatedCRC ");
if (readbuffer[5] == crc8(readbuffer+3)) {
SRH = readbuffer[3] << 8 ;
SRH = SRH + readbuffer[4];
RH = 100.0 * SRH / 65535.0;
}
Serial.println(" temp= "+String(temp,1)+"'C");
Serial.println(" RH%= "+String(RH,0));
delay(5000);
}

unsigned char table[256] ={
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

unsigned char crc8(const unsigned char * data){
unsigned char crc = 0xff;
crc = table[reflect(data[0]) ^ crc];
crc = table[reflect(data[1]) ^ crc];
return reflect(crc);
}

unsigned char reflect(unsigned char c){
unsigned char r=0;
for (int i=0;i<8;i++){
if (c&(1<<i)){
r=r|(1<<(7-i));
}
}
return r;
}

前へ

はじめてのMKR ZERO (9) アナログ温度センサLM35DZを利用

次へ

M5Stackで始めるセンサ・インターフェーシング (1) 次世代距離センサVL53L1X