M5StackGrayで超音波距離センサ(HC-SR04)の動作テスト

2021.6.6 Coskx Lab  

1 はじめに

M5Stack Grayに超音波距離センサHC-SR04(Sparkfun)を接続して周囲の対象物までの距離を測定します。

Grove-Ultrasonic Rangerを使うと,超音波パルスを発生し,対象物まで行って反射して戻るまでの時間を測定し,4m程度までの距離を測定することができます。

2 使用環境

3 接続

マニュアルのピンアサインを見て,電圧を吟味しながら接続します。
コネクタは4本ケーブルです。
(1)トリガ信号線(M5Stack→HC-SR04)
(2)エコー信号線(HC-SR04→M5Stack)
(3)電源5V(M5Stackは5V電源供給できる)
(4)GND

〇トリガ信号線はM5StackのGPIO 19につなぎますが,GPIO 19はSDと共用になっているため,SDを使わない想定で使用しています。
M5Stackは3.3Vで動作していて,トリガ信号線には3.3Vの信号が出ています。(L:0V,H:3.3Vのデジタル信号)
HC-SR04は5V駆動なので,取りが信号も5Vのパルスが期待されています。
MOSFET入力ではH-Lのスレショルドが2から3Vであるため,ノイズマージンは小さいのですが3.3Vの信号はHとして扱ってくれるようです。3.3V入力時の入力FETの内部抵抗はまだ大きいのですがなんとか動作できる程度です。
絶対に誤動作させたくない場合はレベルシフタ(補足3参照)を使って3.3V信号を5V信号に変換するとよいと思います。

〇エコー信号線はM5StackのGPIO 35につなぎますが,GPIO 35はADと共用になっているため,ADを使わない想定で使用しています。
HC-SR04は5Vで動作しているので,エコー信号線には5Vの信号が出ています。(L:0V,H:5Vのデジタル信号)
そのままM5Stackに入力すると,M5Stackにとっては過大電圧になってしまいます。
ESP32のデータシート(*1)によれば,入力ピンへの最大電圧は電源電圧(3.3V)+0.3V=3.6Vとなっていますので,従うべきでしょう。
そこで抵抗を使った抵抗分割で3.3Vの信号に変換してからを作ってM5Stackに与えます。20kΩの抵抗が3つ直列につながれているところがそのからくりになっています。
(入力保護ダイオードを信頼して,20kΩの抵抗を間に挿入するだけという荒業もあるようです。)

*1 https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf



参考 M5Stackの裏側に書いてあるピンの名称

注意 GPIO 19,GPIO 35 を使うことにしているので,他のピンを利用することはできません。

実際の様子 見えにくいのですがブレッドボード上に20kΩ抵抗3個を使った抵抗分割回路があります。


5 テストプログラム

ライブラリの利用も考えられますが,ここは自力で作ります。
超音波発生要求の短いパルスをM5StackからHC-SR04にトリガ信号線で送信すると,HC-SR04は,超音波パルスを発生させ, 超音波パルス発生から超音波パルス反射波到達までの時間を表すパルスをとエコー信号線でM5Stackに返してきます。
M5Stackのプログラムで,「超音波パルス発生から超音波パルス反射波到達までを表すパルス」 の長さを測れば,超音波が対象物まで行って帰る往復時間がわかり,音速を使って対象物までの距離がわかります。
この時間測定にはM5Stackの内部時計が使われ,関数pulseIn(pinForGUR, HIGH)を使って,「pinForGURのピンがHである時間」をμ秒単位で測定します。

  超音波距離センサの動作テスト用プログラム本体

//UltrasonicHCSR04Test01.ino//

#include <M5Stack.h>

const int HCSR04TrigerPin = 19; //使用するGPIOピン
const int HCSR04EchoPin =   35; //使用するGPIOピン

void setup()
{
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Ultrasonic HC-SR04");
  M5.Lcd.setCursor(10, 30);
  M5.Lcd.println("Distance to obstacles");
  pinMode(HCSR04TrigerPin, OUTPUT);
  pinMode(HCSR04EchoPin,INPUT);
}

long measureUSflightDuration() { //[usec]
  digitalWrite(HCSR04TrigerPin, LOW);
  delayMicroseconds(2);
  digitalWrite(HCSR04TrigerPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(HCSR04TrigerPin,LOW);
  long duration = pulseIn(HCSR04EchoPin, HIGH); //[usec] 指示しないのでTimeoutは1秒
  return duration;
}

void loop()
{
  long pulselength; //[usec]
  int RangeInCentimeters; //[cm]
  int RangeInInches; //[inch]
  const double soundvelocity = 34350./1000000.; //[cm/usec at 20 degrees Celsius]
  const double conversion = 0.3937;
  
  pulselength = measureUSflightDuration(); //[usec]
  RangeInCentimeters = pulselength * soundvelocity / 2.;
  RangeInInches = pulselength * soundvelocity / 2. * conversion;
  M5.Lcd.setCursor(0, 46);
  M5.Lcd.printf("%5d inch   \n",RangeInInches);
  M5.Lcd.printf("%5d cm   \n",RangeInCentimeters);
  delay(100);
}

6 測定信号の観察

トリガ信号測定はGPIO 19を使い,エコー信号測定はGPIO 35を使います。
トリガ信号(オシロスコープCh1緑色線)にはM5Stackが発している短いパルスが見え, エコー信号(オシロスコープCh2黄色線)にはHC-SR04が返している「超音波パルス発生から超音波パルス反射波到達までを表すパルス」が見えます。
HC-SR04が出しているエコー信号は5Vのパルス(これは見えていません)ですが,GPIO 35 の観察では抵抗分割の効果で3.3Vのパルスになっているのがわかります。
よく見ると,立ち上がり立下りが若干なまっているのもわかります。20kΩはやりすぎで,数kΩで抵抗分割するのが良かったかもしれません。



7 まとめ

超音波距離センサC-SR04を使用して,4m程度までの距離測定ができました。


補足1 M5Stack Gray 出力ピン割り当てとPWMチャンネル選定テストの結果

1)M5Stack Grayで用途が制限されているGPIOピン
  ・回路図から読み取った,すでに使用されているGPIOピン
   GPIO 1 3 ............. USB UART0 (RXD0,TXD0)で使用
   GPIO 4 23 18 19 ...... SD SPIと共有
   GPIO 33 27 23 18 14 .. LCDで使用
   GPIO 21 22 ........... I2C,電源管理,モーションセンサ,磁気センサで使われている
   GPIO 25 .............. Audioで使用
  ・ESP32仕様
   GPIO 34~39は読み取り専用

2)M5Stack Grayで使えることを確認したGPIOピン
  GPIO 2, GPIO 5 他とは共用していないので自由に使える
  GPIO 16, GPIO 17 UART2 (TXD2・RXD2)を使わなければ OK
  GPIO 26 DAを使わなければ OK
  GPIO 19 SDを使わなければ OK
  GPIO 35, GPIO 36 ADを使わなければ,入力のみOK

3)M5Stack Grayで使ってはいけない PWMチャンネル
  0 音源として使われている (動作させるとビープ音がなる)
  1 用途は不明だが,干渉され周期もデューティ比も指定とは異なる動作をする
  6 LCDの輝度調整に使われている (動作させるとLCDの文字の明るさが変化する)
    使用bit数が12以上のとき,この現象が現れる
  7 LCDの輝度調整に使われている (動作させるとLCDの文字の明るさが変化する)

4)M5Stack Grayで使用確認できたPWMチャンネル
  2,3,4,5,8,9,10,11,12,13,14,15


補足2 超音波センサ操作をクラス化

よく使うプログラムの部品はクラス化して,再利用可能にします。
ここでは,超音波センサ操作部を取り出して,Arduinoのしきたりに合わせて,C++のプログラム部品を作り,使用するときに呼び出すようにします。
ライブラリ化するときは,より一般性を持たせるように作ります。 超音波センサ操作クラスを使うとメインプログラムがすっきりして見通しが良くなります。
通常,スケッチUltrasonicHCSR04OOPTest01.inoは作業用フォルダUltrasonicHCSR04OOPTest01内に作られます。
同じフォルダ内にUltrasonicHCSR04.h,UltrasonicHCSR04.cppを作ります。

 超音波センサで距離測定メインプログラム UltrasonicHCSR04OOPTest01.ino

//UltrasonicHCSR04OOPTest01.ino//

#include <M5Stack.h>
#include "UltrasonicHCSR04.h"

const int HCSR04TrigerPin = 19; //使用するGPIOピン
const int HCSR04EchoPin =   35; //使用するGPIOピン

HCSR04_Ultrasonic mHCSR04_Ultrasonic;

void setup()
{
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Ultrasonic HC-SR04");
  M5.Lcd.setCursor(10, 30);
  M5.Lcd.println("Distance to obstacles");
  mHCSR04_Ultrasonic.setHCUSRPin(HCSR04TrigerPin, HCSR04EchoPin);
}

void loop()
{
  long pulselength; //[usec]
  int RangeInCentimeters; //[cm]
  int RangeInInches; //[inch]
  const double soundvelocity = 34350./1000000.; //[cm/usec at 20 degrees Celsius]
  const double conversion = 0.3937;
  
  pulselength = mHCSR04_Ultrasonic.measureDuration(); //[usec]
  RangeInCentimeters = pulselength * soundvelocity / 2.;
  RangeInInches = pulselength * soundvelocity / 2. * conversion;
  M5.Lcd.setCursor(0, 46);
  M5.Lcd.printf("%5d inch   \n",RangeInInches);
  M5.Lcd.printf("%5d cm   \n",RangeInCentimeters);
  delay(100);
}

 超音波センサ操作クラスヘッダファイル UltrasonicHCSR04.h

//UltrasonicHCSR04.h//

#ifndef HCSR04_Ultrasonic_h
#define HCSR04_Ultrasonic_h

class HCSR04_Ultrasonic {
  public:
    //HCSR04 Ultrasonic Rangerが使用するGPIOピンを設定
    void setHCUSRPin(int trigerPin, int echoPin);
    //超音波が往復にかかる時間を測定
    //時間を[μsec]で返す
    long measureDuration();
  private:
    int trgpin;
    int echpin;
};
#endif

 超音波センサ操作クラス本体ファイル UltrasonicHCSR04.cpp

//UltrasonicHCSR04.cpp//

#include <Arduino.h>
#include "UltrasonicHCSR04.h"

void HCSR04_Ultrasonic::setHCUSRPin(int trigerPin, int echoPin) {
  trgpin = trigerPin;
  echpin = echoPin;
  pinMode(trgpin, OUTPUT);
  pinMode(echpin,INPUT);
}

long HCSR04_Ultrasonic::measureDuration() {
  digitalWrite(trgpin, LOW);
  delayMicroseconds(2);
  digitalWrite(trgpin, HIGH);
  delayMicroseconds(5);
  digitalWrite(trgpin,LOW);
  long duration = pulseIn(echpin, HIGH);;
  return duration;
}

補足3 レベルシフタ回路

3.3V信号を5Vに変換します。トランジスタはNPN型2SC1815などを使用すればよいでしょう。