測定の流れ
今回の温湿度センサーモジュールでは、温湿度を測定するモードとして、単発測定するモードと、自動で周期的に連続して測定するモードがあります。単発測定モードでは、単発測定用コマンドを送信し、測定が完了した後に、データを読み出すといった流れになります。連続測定モードでは、測定開始コマンドで周期的な測定が開始されます。そして、測定値読み出しコマンドを送信してデータの受信、最後に連続測定停止コマンドを送信します。なお各コマンドは、2バイトのデータとなります。
SHT31温湿度センサーモジュール制御クラス
センサーモジュール制御クラスのデータメンバとコンストラクタは、以下のようになります。
// SHT31温湿度センサーモジュール制御クラス public class Sht31 { // 温度 public float Temp { get; private set; } // 相対湿度 public float Rhum { get; private set; } // 受信データバッファ private readonly byte[] Buff = new byte[6]; // I2Cファイルディスクリプタ private int I2cfd { get; } public Sht31(int fd) => I2cfd = fd; }
I2C通信のファイルディスクリプタ、温度、湿度のプロパティ、受信データ用のバイト配列を定義しています。
コマンドを送信するメソッドは、次のように定義しました。
// 単発測定 public void OneshotCmd() { WiringPi.I2CWriteReg8(I2cfd, 0x2C, 0x06); // 単発測定コマンド Read(); // データ受信 } // 連続測定開始 public void StartCmd() { WiringPi.I2CWriteReg8(I2cfd, 0x22, 0x36); // 2回/秒周期で測定 } // 連続測定値読み出し public void ReadCmd() { WiringPi.I2CWriteReg8(I2cfd, 0xE0, 0x00); // 測定値取り込みコマンド Read(); // データ受信 } // 連続測定停止 public void StopCmd() { WiringPi.I2CWriteReg8(I2cfd, 0x30, 0x93); // 連続測定停止コマンド }
連続測定を開始するコマンドでは、測定頻度を指定することができます。今回は、1秒に2回の頻度で連続測定する設定にしました(1秒あたり、0.5、1、2、4、10回の設定が可能)。
OneshotCmdメソッドでは、温湿度測定コマンド送信後、データ読み出しを行います。
データ受信
単発測定用コマンド、または連続測定値読み出しコマンドを送信すると、センサーモジュールからは測定データ(温度・湿度それぞれ2バイト)とチェックサムデータ(それぞれ1バイトCRC)の計6バイトが送られてきます。
データを受信するメソッドは、次のように定義しました。
// データ受信 private void Read() { int cnt = WiringPi.Read(I2cfd, Buff, Buff.Length); // データ受信(6バイト) if (Buff.Length < cnt) return; // 6バイト未満なら終了(1) if (Checksum(Buff[0], Buff[1]) == Buff[2]) // CRC確認 { float rawt = (Buff[0] << 8) | Buff[1]; // 温度データ取り出し(2) Temp = -45 + rawt * 175 / 65535; // 温度に変換(3) } if (Checksum(Buff[3], Buff[4]) == Buff[5]) // CRC確認 { float rawrh = (Buff[3] << 8) | Buff[4]; // 湿度データ取り出し Rhum = 100 * rawrh / 65535; // 湿度に変換 } }
最初に、WiringPi.Readメソッドを使ってデータを受信します。6バイト分受信できなかったら、そこで終了にしています(1)。
センサーモジュールから送られてくるデータ6バイトは、次の順になります。
- 温度測定値(MSB)→ 温度測定値(LSB)→ CRCチェックサム(温度)→湿度測定値(MSB)→ 湿度測定値(LSB)→ CRCチェックサム(湿度)
各測定値のデータ長は、16ビットです。そのためMSB(最上位バイト)の値を8ビットシフトして、LSB(最下位バイト)に加算しています(2)。また測定データは、正の整数値に変換されていますので、正しい値に復元しています(3)。
CRC
各測定値の後には、チェックサム(CRC)データ1バイトが続きます。
このチェックサムデータは、測定値の誤り検出のための符号で、CRCとは、巡回冗長検査(Cyclic Redundancy Check)の略です。CRCでは、データを特定の定数(生成多項式)で割り算し、その余りをデータ確認用の符号として用います。送信側は、この符号を検査データとして付加し、受信側では、同じようにデータを計算して、符号が合致するかどうかを確認します。合致しない場合は、通信上で何らかのデータの破損があったことになります。
CRCのアルゴリズムは、いくつかあります。今回のセンサーモジュールでは、「CRC-8-Dallas/Maxim」と呼ばれる多項式を利用する仕様で、気温、湿度それぞれの16ビットデータから8ビットの値を算出します。
CRCを計算するクラスを、次のように静的クラスとして定義しました。
// CRC-8計算クラス public static class CRC8 { static readonly byte[] table = new byte[256]; // Lookup Table const byte poly = 0x31; // x8 + x5 + x4 + 1 static CRC8() { // Lookup Tableの計算(1) for (int i = 0; i < table.Length; i++) { int temp = i; for (int j = 0; j < 8; j++) { if ((temp & 0x80) != 0) { temp = (temp << 1) ^ poly; } else { temp <<= 1; } } table[i] = (byte)temp; } } // CRCチェックサムの計算 public static byte Checksum(params byte[] bytes) { byte crc = 0xFF; // 初期値 foreach (byte b in bytes) { crc = table[crc ^ b]; } return crc; } }
あらかじめコンストラクタで、1バイト分(0~255の値に対応)の計算結果をテーブルにすることで、都度の計算を省略しています(1)。
Checksumメソッドでは、CRCを計算したいデータ1バイトごとに引数に指定します。例えば、0xBE、0xEFというデータなら、次のようになります。
Console.WriteLine(CRC8.Checksum(0xBE, 0xEF).ToString("X")); // 出力値:92
Sht31クラスのReadメソッドでは、CRCをチェックして、合致しない場合は、気温・湿度を更新しないようにしています。