□超音波測距センサーHC-SR04の使い方

GVCの開発をしはじめた時、というかしているときにOSCで福岡に行ったら、
『ブラインドがふわっとゆれたらそれを検知する方法はないですか?』との
質問を受けました。

自分的には動態検知するものでいいのではないかと思ったのですが、ふわっ
とした時には窓を開けられて侵入されているとのこと。(さすが修羅の国!?)
なので離れた場所から…となると、温かいものが動いたら判る焦電センサー
とかレーザーなどによる測距センサーはどうかな?ということで、いろいろ
調べて、超音波測距センサーのLV-EZ4を当時ポチッた訳ですが、これが妙に
高い。

で、しばらくしてからオープンハードカンファレンスで『HC-SR04というのが
大陸からすごく安く手に入るよ』と教えてもらって調べてみたら、本当に嘘
みたいな値段で入手可能だったので、これを大量にポチッて対応することに
しました。

LV-EZ4は超音波の送信部と受信部が一体型になっているのでコンパクトで、
しかも距離情報の出力方式がいくつもあるのですが、HC-SR04は距離情報が
パルス幅で出てくる
だけで、それを元に計算しなさいということでした。

Arduinoには指定ポートのパルス幅を返してくれる便利な関数がありますが、
GVCで使用しているPICというかXC8にはそんなものはありません。(ある?)

なので、PICの状態変化割り込み(IOCx)を使います。これはあらかじめ指定
したポートの状態がHighからLow、またはその逆になったときに割り込みが
発生する、というものです。

幸い(…というか実際にはあれこれやっていたら気がついたのですが)HC-SR04
が測定できる距離(450cm)というのは、温度20度の理想状態での音速として
13105マイクロ秒くらいで往復する距離になります。

実はGVCでは、PICのタイマーを16MHzでプリスケーラは1:8としているので、
1カウントあたり2マイクロ秒になります。なので16ビットカウンタの最大値
である65536カウントの場合、65536×2us=131072usということで、ちょうど
いい秒数になります。(HC-SR04の中も16ビットタイマで似たような感じで
やっているんでしょうかねー?)

というわけで、現在のGVCではパルスから距離の計算がとてもしやすいです。

測定の手順としては

・あらかじめ状態変化割り込みの設定をする
 (ポートを入力モードに、割り込み発生ポートの指定)
・状態変化割り込みを許可
・HC_SR04のTrigポートをHigh(=1)にする
・10us以上待つ
・タイムアウトタイマースタート
 (Lowのままの場合があるらしいので念のため)
・距離測定タイマースタート
・HC_SR04のTrigポートをLow(=0)にする

	// 状態変化割り込み許可(=1)に
	INTCONbits.RBIE = 1;
	
	// HC_SR04のTrigポートをHigh(=1)にする
	PORT_HC_SR04_TX = 1;
	// 10us以上待てというので30us待つことにする
	Delay_10us(3);
	
	// ------------------------------
	// タイムアウトタイマースタート
	// ------------------------------
	// タイマー0カウンタを初期化
	timer0_count = 0;
	// タイマー値設定
	TMR0H = timer0_h;
	TMR0L = timer0_l;
	// タイマー割り込みフラグ初期化
	INTCONbits.TMR0IF = 0;
	// タイマー0割り込み許可(=1) (Timer0 Overflow Interrupt Enable bit ... INTCON)
	INTCONbits.TMR0IE = 1; 
	// タイマー割り込み開始!!
	T0CON |= 0b10000000;
	// ------------------------------
	// ここまで
	// ------------------------------
	// ステータスLED点灯
	PORT_STATUS_LED = LED_ON;
	
	// ------------------------------
	// 距離測定タイマースタート
	// ------------------------------
	// タイマー1カウンタを初期化
	timer1_count = 0;
	// タイマー値設定
	TMR1H = timer1_h;
	TMR1L = timer1_l;
	// タイマー割り込みフラグ初期化
	PIR1bits.TMR1IF = 0;
	// タイマー1割り込み許可(=1) (Timer1 Overflow Interrupt Enable bit ... PIE1)
	PIE1bits.TMR1IE = 1; 
	// タイマー割り込み開始!!
	T1CON |= 0b00000001;
	
	// HC_SR04のTrigポートをLow(=0)にする
	PORT_HC_SR04_TX = 0;
	

すると、HC-SR04のEchoポートが少ししてからHighになるので、これをPICが
検知して状態変化割り込みが発生するので、

・HC-SR04のEchoポートのデータを取得
・EchoポートがHighなら
 ・距離測定タイマーの値をリセット
・そうではなく、EchoポートがLowなら
 ・状態変化割り込み禁止
 ・タイムアウトタイマー停止
 ・距離測定タイマー停止
・状態変化割り込みフラグ初期化
 (どっちにしても状態変化割り込みが発生したらクリアすること)

	// ----------------------------------------
	// 状態変化割り込み
	// ----------------------------------------
	// 状態変化割り込み(=1)なら (Port B Interrupt-On-Change (IOCx) Interrupt Flag bit ... RBIF)
	if (INTCONbits.RBIF == 1)
	{
		// HC-SR04のEchoポートのデータを取得
		temp_buffer = PORT_HC_SR04_RX;
		// EchoポートがHighなら
		if (temp_buffer == 1)
		{
			// タイマー1カウンタを加算
			timer1_count = 0;
			// タイマー値設定
			TMR1H = timer1_h;
			TMR1L = timer1_l;
		}
		// そうではなく、EchoポートがLowなら
		else
		{
			// 状態変化割り込み禁止
			INTCONbits.RBIE = 0;
			
			// ------------------------------
			// タイムアウトタイマー停止
			// ------------------------------
			T0CON &= 0b01111111;
			// ステータスLEDをOFF
			PORT_STATUS_LED = LED_OFF;
			
			// ------------------------------
			// 距離測定タイマー停止
			// ------------------------------
			T1CON &= 0b11111110;
			// 距離測定タイマーのリザルトLEDもOFFに
			PORT_RESULT_LED = LED_OFF;
			
			// タイマー0カウンターリセット
			timer0_count = 0;
			
			// 距離測定モードをEND(=99)に
			ping_mode = 100;
		}
		
		// 状態変化割り込みフラグ初期化
		INTCONbits.RBIF = 0;
	}

として、Lowになったときに終了フラグをセットするなどして、メインループ
の方で終了フラグにより距離測定タイマーの値を読み出せば、2usでカウント
しているので割り算することなく、HC-SR04から対象物までの片道の音速での
経過時間となる。

あとはWikiPediaとかにも書いてあるけど、計算式にぶち込めば対象物までの
距離が計算できる。

http://ja.wikipedia.org/wiki/音速

ちなみに手持ちのHC-SR04での測定例をあげると

定規 測定  誤差
5cm 4.2cm  約0.8cm
10cm 9.5cm  約0.5cm
15cm 14.1cm  約0.9cm
(20℃として計算)

なので、結構いい加減と思うか正確と思うか!?は皆さん次第です。

まぁ4000円近いものが100円そこそこになるんだから、自分だったら誤差には
目をつぶって使用方法を工夫して誤差をカバーかな? 🙂

AM2321による湿度温度測定

秋月電子でI2Cでデータの取得が出来るAM2321というセンサーが700円で販売され始めたので試してみました。

このデバイスでぐぐると皆さんアドレスを0xB8と言っているのでてっきり10bitアドレッシングなのかと思いましたが、説明書などを読んでみると、I2C的にはどう見ても0x5Cなんじゃないかと思うんですがどうでしょう? そもそもの説明書を書いた人間がI2Cをきちんと理解していないために間違えているのではないかと思うのですが。
(という自分もきちんと理解しているかといわれるとそうではないわけですが…)

しかも、このAM2321からデータを取得する場合などには特別な手順を踏まないといけないのでかなり面倒です。
(判ってしまえばそうですか、というだけなのですが)

というのは、このAM2321、アドレッシングした場合に自分が呼ばれたとしてもACKを返さずにNACKを返すことがあるので、デバイスが接続されているのかどうかを調べる場合には、普通にデータの取得などの手順を踏まないといけないのです。
(呼ばれたことが判るならACK返せよ、まったく…)

しかも、このデバイスに対して連続してデータを要求する場合、500ms位の間隔を空けないとまともにデータの取得が出来ません。 何か特殊用途的な、とりあえず作りました的な、なんだかなぁのデバイスでした。(まぁ取得できるデータは十分すぎますが)

以下のソースはGVC Rev.2を元に、とりあえずテストとして書き起こしているもの(Rev.3にする予定)の一部です。

○I2C スレーブデバイスをスキャン(7bitアドレッシングとして実施)

	// I2C スレーブデバイスをスキャン(7bitアドレッシングとして実施。10bitアドレッシングの場合、最大アドレスは0x3FF=1024)
	for (slave_addr = 1; slave_addr < 0x80; slave_addr ++)
	{
		PORT_RESULT_LED = LED_ON;
		// スレーブアドレスが0x5Cなら、AM2321が繋がっているかもしれないので独自のテストをする(美しくない…)
		if (slave_addr == 0x5C)
		{
			// バッファを初期化
			memset((void *)buff_work, 0x00, sizeof(buff_work));
			// AM2321に起きるようデータを投げる
			i2c_write_test(slave_addr, AM2321_WAKEUP_MSG, 1);
			// AM2321に湿度と温度のデータ要求を投げる
			i2c_write_test(slave_addr, AM2321_GET_MSG, 3);
			// AM2321から湿度と温度ほか、合計8バイトを受信する
			i2c_read(slave_addr, buff_work, 8);
			// 取得したデータの値が正しいなら
			if (buff_work[0] == 0x03 && buff_work[1] == 0x04)
			{
				// I2Cスレーブ状態格納テーブルに格納
				i2c_status_num ++;
				i2c_status_tbl[i2c_status_num].address = slave_addr;
				i2c_status_tbl[i2c_status_num].status = 'o';
			}
		}
		// 対象アドレスで反応があるなら
		else if (i2c_slave_check(slave_addr) == 1)
		{
			// I2Cスレーブ状態格納テーブルに格納
			i2c_status_num ++;
			i2c_status_tbl[i2c_status_num].address = slave_addr;
			i2c_status_tbl[i2c_status_num].status = 'o';
		}
		PORT_RESULT_LED = LED_OFF;
	}

○実際に湿度温度のデータを計算して取得する

		// バッファを初期化
		memset((void *)buff_work, 0x00, sizeof(buff_work));

		// AM2321に起きるようデータを投げる
		i2c_write_test(0x5C, AM2321_WAKEUP_MSG, 1);
		// AM2321に湿度と温度のデータ要求を投げる
		i2c_write_test(0x5C, AM2321_GET_MSG, 3);
		// AM2321から湿度と温度ほか、合計8バイトを受信する
		i2c_read(0x5C, buff_work, 8);

		// 湿度の計算
		humi_data = (buff_work[2] << 8) + buff_work[3];
		humi_vol = humi_data / 10;
		send_strdata("HUMI = ");
		send_intdata(humi_data);
		send_crlf();

		// 温度の計算
		// マイナスなら
		if (buff_work[4] & 0b10000000)
		{
			temp_data = ((buff_work[4] & 0b01111111) << 8) + buff_work[5];
			temp_vol = temp_data / 10;
		}
		// プラスなら
		else
		{
			temp_data = (buff_work[4] << 8) + buff_work[5];
			temp_vol = temp_data / 10;
		}
		send_strdata("TEMP = ");
		send_intdata(temp_data);
		send_crlf();

Tech VillageにてOSHWCのレポートが掲載

電子・組み込み技術の総合サイト:Tech Villageにて、

キッズ&ファミリー歓迎のオープン・ハードウェア展示会,お台場にて開催される ―― オープンハードカンファレンス 2014 Tokyo/Winter
http://www.kumikomi.net/archives/2014/01/rp01ohc.php

として、先日のオープンハードカンファレンスの模様がレポートされています。

単独開催としては初めてだったと思いますが、主催者の秘密結社オープンフォースの河野代表はじめ準備された皆さんのおかげで、とても盛況でした。

懇親会も出たかったのですが体調不良で早仕舞いとさせていただきましたが、次回こそは最後まで!! 🙂

 

01/11 オープンハードカンファレンス@お台場に参加します

今週末、01月11日土曜日にオープンハードカンファレンスが、東京お台場の
テレコムセンタービル東棟14階「MONO」にて開催します。

OPENSOURCE HARDWARE COUNFERENCE
http://oshwc.project2108.com/event/oshwc2014tokyowinter/

現在開発中のGVC:汎用制御装置を展示し、オープンソース・オープンハード
を使った各種の監視や制御を簡単にするシステム(ホームICTなど)をご提案
させていただきます。

またセミナーも15:15から『セミナー会場1』にて開催予定です。

ハードウェアよりのイベントではありますが、皆さんのご来場をお待ちして
おります。

なお、若干ではありますが、GVCのサンプルを用意して販売も行います。

GVC用の基板と、あらかじめプログラムしてあるマスターコントローラー用
のPIC、もしくはご希望のモジュール用のPICのみのセットになります。PIC
以外の部品については秋月やマルツなどのパーツ屋さんでご購入ください。
(簡単な説明書もお付けいたします)

来年はGVCリリース元年!?

すでにお気づきの方もいるかと思いますが、

・進行状況で最新工程表(2013.12.25)
・プロトコルハンドブック(ドラフト版ですね)
・回路&パターン(と、ガーバーデータ一式、Rev:2.1)
・部品一覧(PDF) ←2013.12.27追加

を公開しました。(↑上のメニューから選んでください)

年末年始はRev:2.1の基板でいくつかのモジュールを製作して動態展示環境を充実させるとともに、さらに仕様をつめていき、来年はGVCのリリース元年にしたいと思います。

オープンソース、オープンハードのGVCを来年もよろしくお願いいたします。:-)