Raspberry PI + Windows 10 IoT + C#でI2Cで温湿度センサーに接続する
今回は秋月の HDC1000使用 温湿度センサーモジュール をI2Cで接続して温度と湿度を計測します。
I2C is 何
I2CとはInter-Integrated Circuitの略で近距離通信用の低速※なバスです。名前の通りIC間の通信等電子機器内部の使用を想定しています。
※通信速度は10kbps~3.4Mbpsの複数のモードがありますが、Windows 10 IoTの標準ライブラリでは現状100kbps,400kbpsの2つしか対応していません。
2は上付き文字が正式な名称ですが普通の数字が使われることもあります。読み方はアイ スクエアド シーが正式な読みですがアイ ツー シーと呼ばれることも多いです。WikipediaにはIICと略されることもあると書かれていますが私は見たことがありません。
データ線が2本で外付け回路が2本のプルアップ抵抗だけで済む等回路が簡単であること、1つのバスに複数の機器を接続できること、ホットスワップに対応、特許が2004年に失効していてロイヤリティフリーで使用できること等から広く使われており対応したデバイスも簡単に入手できます。
PC内部デバイス管理に使われるSMBusやPCとモニターの間で解像度等の情報を交換するDDC、変わったところではWiiのコントローラーとヌンチャク等の周辺機器との通信等にも使われています。
I2Cはマスターとスレーブの構成で通信は全てマスターが管理しスレーブから通信を開始することはできません。スレーブは全て7bit(16個の予約アドレスがあるため使用可能なのは112個、10bitの拡張規格もあるけどあまり使われていない)のユニークなIDを持っています。マスターを複数にすることもできますがマスター間、スレーブ間の通信はできません。
用意する物
- Raspberry PI 開発環境一式
- ブレッドボードとか
- HDC1000使用 温湿度センサーモジュール
回路作成
I2Cは2本の信号線にプルアップ抵抗が必要ですが、Raspberry PIはプルアップ抵抗が内蔵されているため接続するだけで使用できます。
秋月のHDC1000モジュールには+V,GND,SDA,SCL,RDYの5つの端子があります。
+V,GNDは電源、SDA,SCLはI2Cです。RDYは今回は未接続でかまいません。
図のように接続します。
プログラム
XAMLにTextBlockを置き温度を表示できるようにします。コードビハインド側に温度、湿度を格納するプロパティを置きINotifyPropertyChangedを実装してプロパティの変更を通知できるようにします。通常のWPFと同じなので解説は省略します。
<TextBlock FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"> 温度:<Run Text="{Binding Temp}"/>°<LineBreak /> 湿度:<Run Text="{Binding Hum}"/>% </TextBlock>
最初にI2Cを使用するためポートを初期化します。
I2Cを使用するにはWindows.Devices.I2c名前空間のクラスを使用します。
いろいろデバイス名を取得したり設定をします。I2cDevice.GetDeviceSelectorの引数はRaspberry PIなら"I2C1"固定です。デバイスによって変わるみたいです。初期化処理は基本的に変更する必要は無いはずです。変更するとしたらBusSpeedくらいだと思います。デバイスが対応していればFastModeが選べます。
using Windows.Devices.I2c; private static async Task<I2cDevice> InitI2cDevice(byte address) { var aqs = I2cDevice.GetDeviceSelector("I2C1"); var device_information_collection = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs); var deviceId = device_information_collection[0].Id; var connectionSetting = new I2cConnectionSettings(address); connectionSetting.BusSpeed = I2cBusSpeed.StandardMode; connectionSetting.SharingMode = I2cSharingMode.Shared; return await I2cDevice.FromIdAsync(deviceId, connectionSetting); }
マニュアル通り初期化コマンドを送信します。0x02 0x10 0x00を送信で温度湿度同時測定になります。
I2CのデバイスIDはマニュアルに記載されています。今回は0x40です。
byte address = 0x40; I2cDevice device = await InitI2cDevice(address); // デバイス初期化 // 測定モードを温度湿度同時にする。 device.Write(new byte[] { 0x02, 0x10, 0x00 });
0x00を送信すると測定を開始し、約13msで値の取得が可能になります。RDYピンの状態を監視すれば変換完了後すぐに取得できますが回路プログラム共に複雑になるので今回は余裕を持って100ms待ってから値を取得するようにします。
// 測定開始 device.Write(new byte[] { 0x00 }); // センサーの測定待ち。測定にに13[ms]かかるので余裕を持って100[ms]待つ。 await Task.Delay(100); var buf = new byte[4]; device.Read(buf); // 仕様書通りに変換 var temp = (buf[0] * 256.0 + buf[1]) / 65536 * 165.0 - 40.0; var hum = (buf[0] * 256.0 + buf[1]) / 65536 * 100.0;
UIスレッド以外からPropertyChangedイベントを発行すると落ちるっぽいのでDispatcher.RunAsyncでUIスレッドに戻します。
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { this.Temp = temp; this.Hum = hum; });
タイマーで定期的に実行するようにすれば温度湿度が表示できます。
今回のプログラム i2c.zip