ESP8266 (ESP-WROOM-02) で距離センサー(HC-SR04)を使う (温度センサーを併用して)

## 概要
ESP8266 (以下、WROOM02) で距離センサーを使う。(音速は気温に影響を受けるので、温度センサーも併用して。)

## はじめに
Arduino で距離センサーを使う場合、以下の素晴らしい記事が、大変参考になりました。
<a href="http://marupeke296.com/EL_Ard_No8_SuperSonic.html" taget="_blank">Arduino編 その８ 距離センサーを使ってみる</a>

これを WROOM02 でやろう、という内容です。



元記事と違う部分は

 - 距離センサーそのものは 5V 駆動なので、WROOM02 の 3.3V でも動作するように回路を修正。
 - 温度センサーを併用して、音速を補正し、距離を正確に求める。

です。

## 部品調達

#### 距離センサー
Amazon で 1個 200円 ( <a href="http://www.amazon.co.jp/%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%88%EF%BC%88SainSmart%EF%BC%89-HC-SR04-%E8%B6%85%E9%9F%B3%E6%B3%A2%E8%B7%9D%E9%9B%A2%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB-For-Arduino/dp/B004U8TOE6" target="_blank">Amazon HC-SR04</a> ) で売ってたので、4個 800円 で大人買いしたら、5個 買えば 780円だったことに後から気づき Orz..
(中国からの輸送なので、変更手続きとかできないと言われた。)

それでも、10日くらいで、無事に 4個 届きました。
![IMG_2396.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/80be76c4-a43f-d0f0-916c-2efe0a3a3ec4.jpeg)

#### 抵抗アレイ
プルアップ(プルダウン)を何箇所もやるのかったるいなぁとおもっていたら、千石の地下１階の突き当りで、「抵抗アレイ」なるものを見つけました。
これは便利！ブレボーに最適！とおもって 10K をまとめ買いしようと思ったら、10K だけ売り切れ Orz.. それに近しいやつを購入してお茶を濁しました。(1個 (8抵抗) 10円)
![IMG_2397.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/a28de977-7690-6a6d-f789-3fa62989ab64.jpeg)


## 回路図

さて、部品もそろったところで。

今回の回路ですが、

 - WROOM02 でセンサーを使う場合は、<a href="http://qiita.com/exabugs/items/9ca4093813db71278b39" target="_blank">ESP8266 (ESP-WROOM-02) でセンサーを扱う</a> を参照してください。
 - 今回は 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にするとアクティブになる)」なので、電圧の考慮は特に要りませんでした。

今回の回路図は以下になります。
![distance.png](https://qiita-image-store.s3.amazonaws.com/0/69410/4c134e43-e33a-9b92-37dc-cc7c9ee38504.png)

Eagleプロジェクトの一式は <a href="https://github.com/exabugs/eagleDistance" target="_blank">Github</a>にアップロードしておきます。

## スケッチ
スケッチは以下になります。

 - 温度センサーによって特性が異なるので、実際の部屋の気温をもとに、Tref の値をずらして、値を校正してください。
 - 一般的な温度センサーは  1℃ で 10mv 変動するみたいですね。(詳しくはデータシートを参照して下さい。)
 - 音速は **331.5 + 気温(℃) * 0.61 (m)** です。

 - <a href="http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2719" target="_blank">map</a>という便利関数が Arduino に用意されているようですが、float 版が無いので真似しました。

 - IO16ピンを RST につないでいるので、Deep-Sleepモード ( <a href="http://qiita.com/exabugs/items/9edf9e2ba8f69800e4c5" target="_blank">ESP8266 (ESP-WROOM-02) で消費電力を抑えるには (スリープモードまとめ)</a> ) を使って消費電力を抑えるのも良いと思います。
 
```cpp
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);
}
```

## 結果

まず、3cm くらいで小手調べ。
![IMG_2394.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/c63015d9-e47b-e2a8-afec-be99a90a453d.jpeg)

適当に連続した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。

![IMG_2391 2.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/e7948f7c-64bb-75ec-39ba-c348acfb8683.jpeg)

かなり、いい線いっています！

```
 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 となっています。
<a href="http://akizukidenshi.com/download/ds/sainsmar/HCSR04_a_2~180cm.pdf" target="_blank">HC-SR04 データシート</a>

限界まで調査したいですが、これ以上は、部屋の片付けが追いつかないので、写真なしで、結果だけお送りします。

上限の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 までは精度よくいけるようです。

## まとめ

 - 主観ですが、予想してたより随分と精度がいいのでビックリです。
 - 気温の変動は少ないだろうから、直近いくつかの最大と最小(異常値の可能性)を除いた平均を使えば、もっと安定すると思います。
 - 距離センサーを使う場面は、乗り物等での自動ブレーキですかね。<br>それなら、200cm でちょうど良い気がします。
 - 個人では、使う場面が思いつきません。。(しかも、4 個も。)
 - Arduino の道を切り開いてくれている先輩方に感謝します。<br>自分は後をトレースしているだけで、申し訳ないです。