概要
ESP8266 (以下、WROOM02) で距離センサーを使う。(音速は気温に影響を受けるので、温度センサーも併用して。)
はじめに
Arduino で距離センサーを使う場合、以下の素晴らしい記事が、大変参考になりました。
Arduino編 その8 距離センサーを使ってみる
これを WROOM02 でやろう、という内容です。
元記事と違う部分は
- 距離センサーそのものは 5V 駆動なので、WROOM02 の 3.3V でも動作するように回路を修正。
- 温度センサーを併用して、音速を補正し、距離を正確に求める。
です。
部品調達
距離センサー
Amazon で 1個 200円 ( Amazon HC-SR04 ) で売ってたので、4個 800円 で大人買いしたら、5個 買えば 780円だったことに後から気づき Orz..
(中国からの輸送なので、変更手続きとかできないと言われた。)
抵抗アレイ
プルアップ(プルダウン)を何箇所もやるのかったるいなぁとおもっていたら、千石の地下1階の突き当りで、「抵抗アレイ」なるものを見つけました。
これは便利!ブレボーに最適!とおもって 10K をまとめ買いしようと思ったら、10K だけ売り切れ Orz.. それに近しいやつを購入してお茶を濁しました。(1個 (8抵抗) 10円)
回路図
さて、部品もそろったところで。
今回の回路ですが、
- WROOM02 でセンサーを使う場合は、ESP8266 (ESP-WROOM-02) でセンサーを扱う を参照してください。
- 今回は A/D 変換の必要なセンサーは 1個 ですが、使い慣れた 8ch 10bit の A/Dコンバータを使っています。
- A/Dが必要なセンサーが1個なら、TOUT 端子を使うのがもっとも部品数が少ないと思いますが、評判が怪しいのでやめてます。
- 余談ですが、A/Dコンバータの ch1〜ch7 は使わないので、GNDに落としますが、ここで先程の抵抗アレイを使うと気持ちいいです。
- 距離センサーからの出力 (ECHO 端子) は 5V です。これを直接 WROOM02 に入力すると大変なこと (実は、リセットがかかるだけ)になるので、気をつけて下さい。
5V を 3.3V にするには、同じ大きさの抵抗 3個を直列に繋いで、1/3 降圧したところで信号を取り出せばいいです。- 3.3 は、よく考えられた数字だということを発見!。
- もし 上限 1V の端子 (例えば TOUT 端子) に入力したい場合は、3.3V をさらに、同じ大きさの抵抗 3個を直列に繋いで、分圧 (2/3 降圧) すればよい。
- TRIG 端子は「負論理端子(その端子を0Vにするとアクティブになる)」なので、電圧の考慮は特に要りませんでした。
Eagleプロジェクトの一式は Githubにアップロードしておきます。
スケッチ
スケッチは以下になります。
-
温度センサーによって特性が異なるので、実際の部屋の気温をもとに、Tref の値をずらして、値を校正してください。
-
一般的な温度センサーは 1℃ で 10mv 変動するみたいですね。(詳しくはデータシートを参照して下さい。)
-
音速は 331.5 + 気温(℃) * 0.61 (m) です。
-
mapという便利関数が Arduino に用意されているようですが、float 版が無いので真似しました。
-
IO16ピンを RST につないでいるので、Deep-Sleepモード ( ESP8266 (ESP-WROOM-02) で消費電力を抑えるには (スリープモードまとめ) ) を使って消費電力を抑えるのも良いと思います。
extern "C"{
#include <spi.h>
#include <spi_register.h>
}
#include <Ticker.h>
Ticker ticker;
const int TRIG = 5;
const int ECHO = 4;
void setup() {
Serial.begin(115200);
Serial.println("");
spi_init(HSPI);
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
ticker.attach_ms(1000, timer);
}
void loop() {
}
void timer() {
float mv0 = check(0);
// 手持ちの温度センサーに応じて、適当にずらして値を校正する
float Tref = -35.97; // MCP9700
//float Tref = -114.68; // LM335Z
// 一般的な温度センサは、10mv/1'C
float tr = mv0 / 10 + Tref;
// パルス発射
digitalWrite(TRIG, HIGH);
delayMicroseconds(100);
digitalWrite(TRIG, LOW);
// 計測
int interval = pulseIn(ECHO, HIGH);
// 温度 tr のときの音速 (m)
float v = 331.5 + tr * 0.61;
// 2 で割るのは往復の時間が計測されているため
// 10,000 で割るのは、cm に換算するため
double distance = interval * v / 2 / 10000; // cm
Serial.print(" Temp.:");
Serial.print(tr);
Serial.print(interval, DEC);
Serial.print("\t");
Serial.print(distance, 2);
Serial.print("\n");
}
// @see: http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2719
float fmap(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float check(int channel) {
// start bit (1 bit) : 1
// SGL/DIFF bit (1 bit) : SGL:1
// select the input channel (3 bit) : CH0:0, CH1:1
uint8 cmd = (0b11 << 3) | channel;
const uint32 COMMAND_LENGTH = 5;
const uint32 RESPONSE_LENGTH = 12;
uint32 retval = spi_transaction(HSPI, 0, 0, 0, 0, COMMAND_LENGTH, cmd, RESPONSE_LENGTH, 0);
retval = retval & 0x3FF; // mask to 10-bit value
// センサー(A/D変換後)の値をmvに変換
// ( 0-1023 を 0-3300 (mV) にマッピングする )
return fmap(retval, 0, 1023, 0, 3300);
}
結果
適当に連続した10個をサンプリング。
- Temp. は室内温度。その隣が距離(cm) になります。
Temp.:24.03177 3.06
Temp.:24.03153 2.65
Temp.:24.03177 3.06
Temp.:24.03177 3.06
Temp.:24.03151 2.61
Temp.:24.03178 3.08
Temp.:24.03178 3.08
Temp.:24.03176 3.05
Temp.:24.03178 3.08
Temp.:24.03177 3.06
次は、50cm。これも適当に抽出した、連続した10回(10秒) です。
± 0.4 % 程度に収まっています。
Temp.:24.032902 50.23
Temp.:24.032880 49.85
Temp.:24.032878 49.81
Temp.:24.032880 49.85
Temp.:24.032904 50.26
Temp.:24.032883 49.90
Temp.:24.032883 49.90
Temp.:24.032882 49.88
Temp.:24.032883 49.90
Temp.:24.352883 49.93
次は、ずっと伸ばして、1m 30cm。
かなり、いい線いっています!
Temp.:24.037469 129.27
Temp.:24.037469 129.27
Temp.:24.357491 129.73
Temp.:24.037466 129.22
Temp.:24.037540 130.50
Temp.:24.037467 129.24
Temp.:24.037541 130.52
Temp.:24.037466 129.22
Temp.:24.037467 129.24
Temp.:24.037494 129.71
HC-SR04 は、データシート上は、2cm から 180cm となっています。
HC-SR04 データシート
限界まで調査したいですが、これ以上は、部屋の片付けが追いつかないので、写真なしで、結果だけお送りします。
上限の200cm (180cm と間違えて 200cm で計測してしまいました。)
しかし、これも問題無いようです。
(気温センサのミスを除くと、200cm で ± 5mm)
Temp.:23.3811566 199.96
Temp.:23.3811569 200.01
Temp.:23.3811567 199.97
Temp.:23.3811566 199.96
Temp.:23.0611568 199.88
Temp.:23.0611591 200.27
Temp.:23.3811541 199.52
Temp.:23.3811540 199.51
Temp.:23.3811596 200.47
Temp.:23.3811570 200.02
Temp.:33.0611573 203.49 ← 気温がダメ
Temp.:23.3811596 200.47
Temp.:23.3811569 200.01
Temp.:23.7111620 201.00 ← 気温がダメ
Temp.:23.3811540 199.51
205cm 。あぁ、そろそろ、だめっぽいです。
Temp.:22.7411828 204.25
Temp.:23.0611827 204.35
Temp.:23.0611854 204.82
Temp.:23.0611852 204.78
Temp.:23.0611827 204.35
Temp.:33.7111831 208.26 ← 気温がダメ
Temp.:22.7411827 204.24
Temp.:23.0611878 205.23
Temp.:23.0611829 204.39
Temp.:22.7411852 204.67
210cm 。これはもう、ダメですね。力尽きました。
Temp.:22.7411948 206.32
Temp.:22.7411899 205.48
Temp.:22.7411951 206.38
Temp.:22.7411926 205.94
Temp.:23.0611953 206.53
Temp.:22.7411927 205.96
Temp.:23.0611952 206.51
Temp.:22.7411925 205.93
Temp.:23.0611977 206.94
Temp.:23.0611953 206.53
データシート上は 2cm 〜 180cm ですが、200cm までは精度よくいけるようです。
まとめ
- 主観ですが、予想してたより随分と精度がいいのでビックリです。
- 気温の変動は少ないだろうから、直近いくつかの最大と最小(異常値の可能性)を除いた平均を使えば、もっと安定すると思います。
- 距離センサーを使う場面は、乗り物等での自動ブレーキですかね。
それなら、200cm でちょうど良い気がします。 - 個人では、使う場面が思いつきません。。(しかも、4 個も。)
- Arduino の道を切り開いてくれている先輩方に感謝します。
自分は後をトレースしているだけで、申し訳ないです。