さくらのジャンク箱ロゴ

Sakura87がほぼ月刊でお届けするPCや電子工作と写真のブログ @なんと20周年

Raspberry Pi 2 で I2C接続の温湿度センサーを使用する(HDC1000)

この記事は9年ほど前に投稿されました。内容が古くなっている可能性がありますので更新日時にご注意ください。

IMG_7139_20151223

久しぶりのRaspberry Pi2の記事です。

今回は以前購入して放置していた、I2C接続の温湿度センサーを使ってみたいと思います。

必要なもの

14bitの精度で低消費電力なI2C接続の温度センサーICであるHDC1000を今回は使用します。
用意するものはこのセンサーが採用されたモジュール1つのみです。

今回用意したのは秋月電子で購入できる HDC1000使用 温湿度センサーモジュール AE-HCD1000 を使用しました。

このモジュールを使用すれば各IO用のプルアップ抵抗も実装されていますので外部部品が不要です。

I2C接続とは

1980年代初期にフィリップスが自社製品で使用するために開発した規格で1992年に標準化され、その後何度か改良されている通信方式。

制御は2本の信号線(データ線とクロック信号)とGNDのみという非常にシンプルな設計。
(機器の電源を入れて最低4本で制御可能)
この2本の信号線で双方向通信およびアドレス指定によって最大112個の機器を接続できます。

制御電圧は最高で5V、基本的に3.3V・5V及び3.3~5Vの電源に対応した機器が多いようです。

転送速度は基本的には100kbps、10kpbsの低速モードや400kbpsの高速モード、最近の規格では数Mbpsの通信にも対応している模様。

このモジュールは400kbpsまで対応 Raspberry Piも同様の模様(BCM2835のデータシートによるとバージョン2.1まで対応。ただし転送速度は400kまで。 2は2836なので仕様が違うかもしれないが。)

前準備

I2C接続を使用するにはSPI同様にラズビアンにて設定変更が必要です。

OSの設定

「raspi-config」にて8 Advanced Options→A7 I2C→はい×2 を実行したあと、再起動してI2Cを有効にします。

/etc/modules の確認

/etc/modulesを開いて以下の2行が記述されていることを確認し、なければ追加します。

i2c-dev
i2c-bcm2708

必要モジュールのインストール

「libi2c-dev」をapt-getでインストールします。
なお、今回WiringPiも使用するので、もしインストールしていない場合は以下の記事を参考にインストールしてください。

接続

pi2gpio

今回購入した秋月電子のモジュールでは、表(ICチップのある方)の左側(型式明が書いてある方)が1番となっており、ピンアサインは以下のとおりです。

ピン番号 信号名 説明
Vcc 電源入力(+3.3Vに接続)
SDA データ転送ピン(双方向)
SCL クロック入力
RDY 変換終了信号
GND

見て分かる通り、Raspberry PiのI2C出力そのままなのでπとモジュールで1→1 3→2 5→3 7→4 9→5と接続するのがよいでしょう。ピン配列が同じなのでピンソケットを付けて接続してもいいのですが。正確な室温を取得するためになるべくRaspberry Piやその他の機器から離し、外気に露出するところで直射日光の当たらない場所に設置した方がいいでしょう。

IMG_7140_20151223

モジュールに付属のピンヘッダを取り付け

IMG_7142_20151223

接続例

モジュールアドレスの確認

シェルにて「i2cdetect -y 1」というコマンドを実行すると以下の様なリストが表示されます。
今回はこのモジュールしか接続していないので1つのみ出ているはずです。なお、秋月のモジュールのアドレスは0x40固定です。

i2cdetect

モジュール動作確認

接続したモジュールが正常に動作しているかを確認するためにダンプを取ってみたいと思います。

シェルにて「i2cdump -y 1 0x40 w」と入力して最後に以下の様な数字が入っていればモジュールは正常に動作しています。なお、Raspberry Piにてコンソール上で取得できるデータは何故か上位バイトと下位バイトが入れ替わっているので、例えば実際の値が0x1234という数値の場合0x3412と入れ替わって取得されます。入れ替わるのはバイト単位でビット単位での入れ替わりはありません。(ただし実際にC言語などで制御する場合は入れ替わらないので注意)

  • f8: XXXX XXXX XXXX ec02 91ec 005f 4954 0010

前3つのXXXXは値が取得できなかったもの。その次の3つの数値はモジュールのシリアルコードなので使用するモジュールによって違います。最後の2つは1つ目がマニュファクチャIDで固定の値となっています。
このモジュールはテキサス・インスツルメンツ製なのでTとIのアスキーコードである0x54と0x49で0x5449という数値が入っています。一番最後の数値はデバイスIDとなっていて、これもHDC1000なので0x1000となり固定となっています。

制御方法

電源を入れてからデバイスの起動におよそ15msかかりますので、15ms待ってから設定データを送信します。(Raspberry Piの電源から取る場合、基本的にOSが起動する間に完了するので気にしなくてもいいです。)送信後に読み込みたいデータが格納されているポインタのアドレスを送信してRDYピンがL(0)になるのをまちます。

ピンがLになったらデータを受信すると温湿度データ受信できます。

受信データは上記の設定によって温度データ(16bit)・湿度データ(16bit)・温湿度データ(32bit)と言ったデータを温度計が送信します。

ポインタ 名称 役割
0x00 Temperature 温度データ格納
0x01 Humidity 湿度データ格納
0x02 Configuration 設定データ格納

ポインタアドレスは以上のようになっています。

最初に0x02に対して設定データを送信して次に読みだす測定値にあったポインタのアドレスを指定します。
0x02に送信する設定データは以下のとおりです。

bit 名称 用途
0 1
15 RST ソフトウェアリセット 通常動作(リセットはされない) リセット(リセット後0になる)
14 Reserved 未使用 常時0設定
13
12 MODE モード設定 温度と湿度を個別取得
(16bit長 ポインタ指定で切り替え)
温度と湿度を同時取得
(32bit長 温度・湿度の順)
11 BTST 電源状態の設定 電源が2.8V以上の場合
(Raspberry Piは常にこちらを使用)
電源が2.8V以下の場合
10 TRES 温度の分解能設定 14bit 11bit
9 HRES 湿度の分解能設定 両方0:14bit
9が0で8が1:11bit
9が1で8が0:8bit
8

※bit7~0は全てReservedとなり、常時0となるので省略しました。
今回は温湿度ともに分解能が14bit、温度と湿度を同時取得で初期化するので設定値は「0x1000」となります。

取得手順

このモジュールは以下のとおりに制御します。

  1. 電源投入
  2. 15ms待つ
  3. 設定レジスタアドレス(0x02)を送信してデータ送信先を決定
  4. 3で設定したアドレスに(0x1000)を送信する
  5. 温度データ格納レジスタのアドレス(0x00)を送信してデータ送信モードにする
  6. RDYピンの状態を監視してLになるまで待機
  7. RDYピンがLになったらデータを受信する
  8. 受信データを温度と湿度にわけ、指定の計算式で計算する。
  9. 連続して測定する場合は5~8(緑色の部分)をループさせる

温度データの計算式は以下のとおりです。

  • 温度(℃)=(取得データ÷216)×165℃-40℃

コレをプログラムにする場合、216はどうせ固定値なので事前に計算しておきます。

  • 温度=(取得データ / 65536.000 ) * 165.000 – 40.000

湿度データの計算式は以下のとおりです。

  • 湿度(%)=(取得データ÷216) × 100.000

温度と同様に216は事前に計算しておきます。

  • 湿度=(取得データ / 65536.000) * 100.000

なお、I2Cの制御に今回はWiringPiのI2Cライブラリを使用しますが。これを使用した場合はWiringPiのポート番号ではなくOS標準のポート番号で指定しないといけないようです。なので使用する物理7番ピンはいつもなら7番を指定するところですが、今回は4番を指定します。

コーディング

今回はライブラリを使用するので特に複雑な処理はしていません。

関数もデータ取得用と初期化用で分けたくらいです。

WiringPiとWiringPiのI2C用ライブラリをインクルードして上の用法通りに制御するだけです。

#include <stdio.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>

// デバイスアドレス 
// i2cdetect -y 1 で調べられる
#define DevAddr 0x40

// RDY ピンを接続しているIOピン設定
// 何故かOS標準のピン番号で指定しないと使えない。
// OS標準の番号で4番のIOピンならRaspberry Pi2の物理7番ピンなので
// 秋月のモジュールならポン付け出来るはず。
#define RDPin 4


// 温度取得ポインタとConfigポインタのアドレス指定。
// 基本的にこのままでOK。今回は温度と湿度を同時取得するので
// 温度側だけでOK
#define TempP 0x00
#define ConfP 0x02

// デバイスの設定
// 2バイトの設定データを1バイトに分けて設定。
// 1=上位ビット側 2=下位ビット側
// 0x1234という設定データなら 1=0x12 2=0x34と設定
#define DevConf1 0x10
#define DevConf2 0x00


// プロトタイプ宣言
int HDC_get(int,char);
int HDC_init(void);

設定関係を宣言します。詳細はコメントに書いてあるので飛ばします。
なお、設定データの値は上位バイトと下位バイトで分けたほうが後々都合がいいので分けます。
といっても下位バイトは常に0x00なので上位バイトだけ指定してもいいかもしれません。

次に初期化コードを書きます。

// 温湿度センサー初期化命令
// 初期化が成功したらファイルハンドルを返すので変数で捕まえる。
int HDC_init(void){

	
	int fd = wiringPiI2CSetup(DevAddr);
	
	unsigned char ConfData[3] = {ConfP,DevConf1,DevConf2};
	
	wiringPiSetupGpio();
	pinMode(RDPin,INPUT);

	if(write(fd,ConfData,3)<0){printf("センサー設定エラー\n");return -1;}
	
	return fd;
	}

wiringPiI2CSetupにてデバイスアドレスを指定して初期化します。この関数はファイルハンドルを返すのでInt型でキャッチします。

次にキャラクタ型の配列変数を用意してそこに設定データ格納用のポインタアドレス、設定データ上位バイト、設定データ下位バイトの順で格納します。

次にいつもどおりGPIOを設定します。RDY入力用のピンを入力で初期化します。
プルアップ・ダウンの設定はモジュールに内蔵されていますのでオフでOKです。

次に設定データを格納した変数をwrite命令で転送します。
write命令はunistd.hの標準命令でWiringPiの命令ではありませんが、参考資料のサイトがこの方法で記述しており、wiringPiの読み書き命令もデータをこの命令に中継しているだけだったのでこうしております。

次に温湿度を実際に取得する命令を書きます。

上のチャートの5~7と8の前半部分の処理をしています。

// 温度取得命令
// fd=ファイルハンドル
// data_type=戻りデータ選択(0=温度 1=湿度)
// 戻り値はセンサーから帰ってきた生データなのでそのデータを
// もとに計算する。-1の時はエラーが発生している。
int HDC_get(int fd,char data_type){

	unsigned char ReData[4];
	unsigned char GetData[1]={TempP};
	
	// センサーに温度測定要求
	if(write(fd,GetData,1)<0){printf("データ要求エラー\n");return -1;}
	
	// センサーが測定→変換を完了させるのを待つ
	while((digitalRead(RDPin))==1);

	// データ取得
	if(read(fd,ReData,4)<0){printf("データ受信エラー\n");return -1;}
	
	// 戻ってきたデータをくっつけて返す
	if (data_type == 0) return (ReData[0]<<8)|(ReData[1]);
	if (data_type == 1) return (ReData[2]<<8)|(ReData[3]);
	return -1;
	}

ファイルハンドルと取得したいデータのタイプを指定してこの関数を実行すると、センサーのデータを取得して指定したデータの生データを返します。

取得したデータは生の値であるため取得後にこれを計算してやる必要があります。
なお、エラーの場合は-1を返します。所得するデータは14bit値でMSB詰めなので、-1にはならないはずなので。

返すデータは単純に上位バイトを8bit底上げしたものに下位バイトをくっつけているだけです。

最後にmain関数を作ります。

int main(void){
	
	// デバイスの初期化。
	int fd = HDC_init();
	if(fd==-1){return 1;}
	printf("現在の気温と湿度\n");
	
	// 値の取得 連続してデータを取得するならここからループさせる
	double TempVal = (HDC_get(fd,0) / 65536.000 ) * 165.000 - 40.000 ;
	double HumiVal = (HDC_get(fd,1) / 65536.000 ) * 100.000;
	
	// 表示
	printf("気温:%.2f℃  湿度:%.2f%\n",TempVal,HumiVal);
	return 0;
	}
	

デバイスを初期化して、気温と湿度を取得し、計算結果を実数型で格納し表示しています。

最後に

今回はI2C接続の温度計を使ってみましたが。やはりIoTを利用するのならセンサー関係は必須となるでしょう。温度センサーは種類が豊富でいろいろとありますが。I2Cで制御がしやすく、小型で精度も悪くなくて値段も手頃なのでこのセンサーにしました。他には1ワイヤ方式や電圧出力方式といった方式も存在しますが。

1ワイヤは基本的にはセンサ系が多く、電圧出力方式はRaspberry PiだとAD変換ICを使う必要が有るため今回はセンサー以外のものを制御するのにも応用の効くI2C接続のセンサーを採用しました。

I2C接続ならキャラクタ液晶やEEPROM、モータードライバなんかもあるようなので一度使用方法を学んでしまえば応用の幅が広がりますね。

全体のコード

#include <stdio.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>

// デバイスアドレス 
// i2cdetect -y 1 で調べられる
#define DevAddr 0x40

// RDY ピンを接続しているIOピン設定
// 何故かOS標準のピン番号で指定しないと使えない。
// OS標準の番号で4番のIOピンならRaspberry Pi2の物理7番ピンなので
// 秋月のモジュールならポン付け出来るはず。
#define RDPin 4


// 温度取得ポインタとConfigポインタのアドレス指定。
// 基本的にこのままでOK。今回は温度と湿度を同時取得するので
// 温度側だけでOK
#define TempP 0x00
#define ConfP 0x02

// デバイスの設定
// 2バイトの設定データを1バイトに分けて設定。
// 1=上位ビット側 2=下位ビット側
// 0x1234という設定データなら 1=0x12 2=0x34と設定
#define DevConf1 0x10
#define DevConf2 0x00


// プロトタイプ宣言
int HDC_get(int,char);
int HDC_init(void);

int main(void){
	
	// デバイスの初期化。
	int fd = HDC_init();
	if(fd==-1){return 1;}
	printf("現在の気温と湿度\n");
	
	// 値の取得 連続してデータを取得するならここからループさせる
	double TempVal = (HDC_get(fd,0) / 65536.000 )* 165.000 - 40.000 ;
	double HumiVal = (HDC_get(fd,1) / 65536.000 ) * 100.000;
	
	// 表示
	printf("気温:%.2f℃  湿度:%.2f%\n",TempVal,HumiVal);
	return 0;
	}
	

// 温湿度センサー初期化命令
// 初期化が成功したらファイルハンドルを返すので変数で捕まえる。
int HDC_init(void){

	
	int fd = wiringPiI2CSetup(DevAddr);
	
	unsigned char ConfData[3] = {ConfP,DevConf1,DevConf2};
	
	wiringPiSetupGpio();
	pinMode(RDPin,INPUT);

	if(write(fd,ConfData,3)<0){printf("センサー設定エラー\n");return -1;}
	
	return fd;
	}
	

// 温度取得命令
// fd=ファイルハンドル
// data_type=戻りデータ選択(0=温度 1=湿度)
// 戻り値はセンサーから帰ってきた生データなのでそのデータを
// もとに計算する。-1の時はエラーが発生している。
int HDC_get(int fd,char data_type){

	unsigned char ReData[4];
	unsigned char GetData[1]={TempP};
	
	// センサーに温度測定要求
	if(write(fd,GetData,1)<0){printf("データ要求エラー\n");return -1;}
	
	// センサーが測定→変換を完了させるのを待つ
	while((digitalRead(RDPin))==1);

	// データ取得
	if(read(fd,ReData,4)<0){printf("データ受信エラー\n");return -1;}
	
	// 戻ってきたデータをくっつけて返す
	if (data_type == 0) return (ReData[0]<<8)|(ReData[1]);
	if (data_type == 1) return (ReData[2]<<8)|(ReData[3]);
	return -1;
	}

参考資料

総閲覧数:202 PV

関連記事

“Raspberry Pi 2 で I2C接続の温湿度センサーを使用する(HDC1000)” への1件のコメント

コメントを残す

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

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