温湿度センサーSHT31を使う場合の注意点(パート1)

センシリオン社製温湿度センサーSHT31をArduinoのI2C経由で制御して評価していたのですが(写真1)、いろいろと「はまって」しまいました(苦笑)。そのときのトラブルとその解決法を備忘録として以下に記載します。

写真1 センシリオン製温湿度センサーSHT31を、
Keysightデジタルオシロスコープ(DSOX2014A)で評価している様子

なお、トラブルが多岐にわたったのと、それぞれのトラブルに対する解決策及びその妥当性の検証に時間がかかったため、何回かに分けて詳細を記載したいと思います。

今回はその第一回です。

 

● Writeコマンドを連続して送ってはいけない。待ち時間が必要。

ある程度間隔をおいてコマンドを送らないと、SHT31が「コマンドを解釈しました」というACKを返さないようです。ACKが返ってこないということは、そのコマンドがSHT31に解釈されていない可能性が高いです。
代わりのNACKを返すのですが、ArduinoのWireライブラリがそのNACKをうまく処理できないため(私のプログラミング能力が劣っているのかもしれませんが…)I2C通信が止まってしまう場合がありました。

少々長いですがトラブルを起こしたプログラムの例とその解決法を以下に示します。なおコマンドの詳細に関しては、SHT31のデータシートを参照ください。

トラブルを起こしたプログラムは以下。

#include <Wire.h>
int SHT31_Address = 0x45;// SHT31のI2Cアドレス(7ビット)
(中略)

void setup()
{
Wire.begin(); //Wireライブラリを使って、I2Cの有効化
Serial.begin(9600); //シリアル通信スピードを9600bpsに設定

//ソフトウエアリセットコマンド送信
Wire.beginTransmission(SHT31_Address);
Wire.write(0x30);
Wire.write(0xA2);
Wire.endTransmission();

//ステータスレジスタクリア
Wire.beginTransmission(SHT31_Address);
Wire.write(0x30);
Wire.write(0x41);
Wire.endTransmission();

//内部ヒーターOFF
Wire.beginTransmission(SHT31_Address);
Wire.write(0x30);
Wire.write(0x66);
Wire.endTransmission();}

void loop()
{
//SHT31にI2C経由でコマンド送信開始
Wire.beginTransmission(SHT31_Address);
//シングルショットモードで測定
//MSB部のコマンド。0x24はクロック・ストレッチ無効とする
Wire.write(0x24);
//LSB部のコマンド。0x00は繰り返し精度「高」
Wire.write(0x00);
//コマンド送信終了
Wire.endTransmission();

//SHTー31に対して、I2C経由で6バイト分のデータを読むようにリクエスト
Wire.requestFrom(SHT31_Address,6);
//6バイト準備されるまで待つ
while (Wire.available()<6){
}
//読み込み開始
Ta_MSB=Wire.read();
Ta_LSB=Wire.read();
Ta_CRC=Wire.read();
RH_MSB=Wire.read();
RH_LSB=Wire.read();
RH_CRC=Wire.read();
(以下略)

このときのI2Cバスの波形を図1に示します。I2C通信で失敗した例

図1 Writeコマンドを連続して送り、I2C通信が止まった例

 デジタルオシロスコープのI2Cデコード機能を使ってI2Cバス上でやり取りされているデータを観測すると、

45Wa 30a A2a    45Wa 30a 41~a    45Wa 30a 66a    45Wa 24a 00~a    45R~a

となっています。

「45Wa」は、アドレス45のスレーブデバイス(ここではSHT31)に対してWrite(W)を行う旨通知し、ACK(a)が返ってきたことを示しています。その後の「30a A2a」は、コマンド0x30A2(ソフトウエアリセット)がSHT31に送られ、それぞれにACK(小文字のa)が返ってきて、それらのコマンドをSHT31が了解したことを示します。

問題は、45Wa 30a 41~aです。
45Wa 30a」は アドレス45のスレーブデバイス(SHT31)に対してWriteを行う旨を通知してACKが返り(45Wa)、次の文字列0x30まではACKを返して(30a)了解した旨をマスター(Arduino)に通知しているのですが、続く0x41に対しては「41~a」とNACKを示す「~a」が表示されています。これは、ステータスレジスタクリア命令、「0x3041」が実行されていないことを示しています。

続く「45Wa 30a 66a」は、ACKを示す「a」が表示されているので「内部ヒーターOFF」のコマンド「0x3066」が問題なく実行されていることを示しています。

その次の「 45Wa 24a 00~a」は、温湿度を、「I2Cクロックストレッチ無し」かつ「高精度」で測定するためのコマンド「0x2400」を送ったものの、00の次にNACKを示す「~a」が観測されており、測定が実行されなかったことがわかります。
そのため、データ読み出しコマンド「45R」を送っても、測定データ受信ができなかったことを示すNACK(~a)を返してI2Cバスが止まってしまいました。

この状態を避けるための方策をSHT31のデータシートから読み取ろうとしたのですが特に記載がなく、「Writeコマンドを送った後には、そのコマンドを解釈する時間が必要」という結論にいたり、各Writeコマンドの後にdelay()を入れることとしました。

どの程度の待ち時間が適切なのか?が分からなかったので、秋月電子が販売しているSHT31モジュールのサンプルプログラム(Arduinoスケッチ及びライブラリ)を参考にしました。以下にそのサンプルプログラムの一部を示します。

void AE_SHT31::SoftReset(void)
{
i2c_write(0x30A2);
delay(500);
i2c_write(0x3041);
delay(500);
}

void AE_SHT31::Heater(uint8_t onoff)
{
if(onoff==1)i2c_write(0x306D);
else i2c_write(0x3066);
delay(500);
}

(中略)

void AE_SHT31::GetTempHum(void)
{
uint8_t data[7];

i2c_write(0x2400);
delay(300);

秋月電子のプログラム(AE_SHT31.cpp)では、ソフトウエアリセット(0x30A2)、ステータスレジスタクリア(0x3041)、内部ヒーターOFF(0x3066)の後に500msの、測定開始コマンド(0x2400)の後には300msの待ち時間をそれぞれ設けていることが分かりました。

これらの待ち時間を私のプログラムに反映して得られたI2Cバスの波形を図2-1~図2-5に示します。

図2-1 ソフトウエアリセットコマンド(0x30A2)を送った時の波形。ACKが返ってきている。

図2-2 ステータスレジスタクリアコマンド(0x3041)を送った時の波形。
先ほどはNACKを返していたが、今回はACKを返している。

図2-3 内部ヒーターOFFコマンド(0x3066)を送った時の波形。これもACKが返ってきている。

図2-4 温湿度測定コマンド(0x2400)を送った時の波形。
先ほどはNACKを返していたが、今回はACKを返している。

図2-4 データの読み出し。先ほどはNACKを返したところでバスが停止していたが、SHT31がACKを返して、測定データをバス上に流している。

PC上のCOMにも、測定データが表示され、正常動作していることが分かります(図3)。

図3 測定データがCOMに表示された。

ここまでで、問題なく動作することが確認されたのですが、delay()に入れるべき値が妥当なのかどうなのか?が気になるところです。

今回入れた500ms及び300msが果たして妥当な値なのかどうなのかを、次回で検討したいと思います。Part2に続く

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください