M5StackGrayで超音波距離センサの動作テスト

2021.5.28 Coskx Lab  

1 はじめに

M5Stack Grayに超音波距離センサGrove-Ultrasonic Ranger(Seeed Technology)を接続して周囲の対象物までの距離を測定します。

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

2 使用環境

3 接続

マニュアルのピンアサインを見てそのままつなげるだけです。
コネクタは4本ケーブルですが,白ケーブルは使いません。電源3.3V,GND,信号線のみ使います。
信号線はM5StackのGPIO 19につなぎますが,GPIO 19はSDと共用になっているため,SDを使わない想定で使用しています。



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

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


実際の様子

5 テストプログラム

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

measureUSflightDuration()中で,測定が終わり次第,GIOPピンを出力設定しています。 この作業をしないと次の測定指令用の短いパルスを発するまでは,ピンが入力設定のままになります。 超音波センサ側では次の測定指令用の短いパルスを受け入れるために入力設定に切り替わります。(いつ切り替わるのか,データシートに記載はありません。)
GPIOピン,センサ端子が直結されている状態で,ともに入力状態になると,モータなどを駆動している場合, ノイズパルス受け入れの可能性が高くなります。 そうすると,センサはノイズパルスで誤動作する可能性が出てきます。これを防ぐために,測定が終わり次第第,GIOPピンを出力設定しています。

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

//UltrasonicRangerTest01.ino//

#include <M5Stack.h>

const int groveUltrasonicRangerPin = 19; //使用するGPIOピン

void setup()
{
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Grove Ultrasonic Ranger");
  M5.Lcd.setCursor(10, 30);
  M5.Lcd.println("Distance to obstacles");
}

long measureUSflightDuration(int pinForGUR) { //[usec]
  pinMode(pinForGUR, OUTPUT);
  digitalWrite(pinForGUR, LOW);
  delayMicroseconds(2);
  digitalWrite(pinForGUR, HIGH);
  delayMicroseconds(5);
  digitalWrite(pinForGUR,LOW);
  pinMode(pinForGUR,INPUT);
  long duration = pulseIn(pinForGUR, HIGH); //[usec] 指示しないのでTimeoutは1秒
  pinMode(pinForGUR, OUTPUT);
  digitalWrite(pinForGUR, LOW);
  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(groveUltrasonicRangerPin); //[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を使います。この信号にはM5Stackが発している短いパルスと,Grove-Ultrasonic Rangerが返している「超音波パルス発生から超音波パルス反射波到達までを表すパルス」が含まれています。

(1)測定物までの距離が10cmの場合


(2)測定物までの距離が166cmの場合


7 まとめ

超音波距離センサGrove-Ultrasonic Rangerを使用して,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++のプログラム部品を作り,使用するときに呼び出すようにします。
ライブラリ化するときは,より一般性を持たせるように作ります。 超音波センサ操作クラスを使うとメインプログラムがすっきりして見通しが良くなります。
通常,スケッチUltrasonicRangerOOPTest01.inoは作業用フォルダUltrasonicRangerOOPTest01内に作られます。
同じフォルダ内にGrove_Ultrasonic.h,Grove_Ultrasonic.cppを作ります。

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

//UltrasonicRangerOOPTest01.ino//

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

const int groveUltrasonicRangerPin = 19; //使用するGPIOピン
Grove_Ultrasonic mGrove_Ultrasonic;  //Grove_Ultrasonicのインスタンス

void setup()
{
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.println("Grove Ultrasonic Ranger");
  M5.Lcd.setCursor(10, 30);
  M5.Lcd.println("Distance to obstacles");
  mGrove_Ultrasonic.setGUSRPin(groveUltrasonicRangerPin);
}

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 = mGrove_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);
}

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

//Grove_Ultrasonic.h//

#ifndef Grove_Ultrasonic_h
#define Grove_Ultrasonic_h

class Grove_Ultrasonic {
  public:
    //Grove Ultrasonic Rangerが使用するGPIOピンを設定
    void setGUSRPin(int GUSRPin);
    //超音波が往復にかかる時間を測定
    //時間を[μsec]で返す
    long measureDuration();
  private:
    int pin;
};
#endif

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

//Grove_Ultrasonic.cpp//

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

void Grove_Ultrasonic::setGUSRPin(int GUSRPin) {
  pin = GUSRPin;
}

long Grove_Ultrasonic::measureDuration() {
  pinMode(pin, OUTPUT);
  digitalWrite(pin, LOW);
  delayMicroseconds(2);
  digitalWrite(pin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pin,LOW);
  pinMode(pin,INPUT);
  long duration;
  duration = pulseIn(pin, HIGH);;
  pinMode(pin, OUTPUT); //pinを出力に(ノイズ対策)
  digitalWrite(pin, LOW); //pinにLを出力
  return duration;
}