概要
超音波センサーHC-SR04等を用いてArduinoで距離計測を行いたいときに、Trigピンで超音波を発生させた後にEchoピンで、超音波が反射されてくるまでの時間を計測することで距離を計測できます。その際、pulseInという関数で時間を計測できます。
https://rb-station.com/blogs/article/hc-sr04-arduino
サンプルコードの詳細は上記の記事をご確認ください。
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite( TRIG, HIGH );
delayMicroseconds( 10 );
digitalWrite( TRIG, LOW );
duration = pulseIn( ECHO, HIGH ); // 往復にかかった時間が返却される[マイクロ秒]
上記はサンプルコードの抜粋ですが、pulseInで超音波が反射して戻ってくるまでの時間を計測します。しかし、pulseInを実行している間、他の処理は止まってしまいます。
数マイクロ秒なので、気にならないことも多いですが、例えば同時にステッピングモータを動作させているときはモータ動作がpulseInをしている間止まってしまって滑らかでなくなってしまいます。
そこで、pulseInは使わず、他の処理は止めることなく超音波センサでの距離計測ができる方法を紹介します。
関数を自作する
https://arduino.stackexchange.com/questions/28816/how-can-i-replace-pulsein-with-interrupts
上記の記事内容がほぼ答えになっていますが、pulseInのように入力があるのを待っているのではなく、定期的なループごとに入力があるかを毎回チェックする形で対応できます。
/* * Non-blocking pulseIn(): returns the pulse length in microseconds * when the falling edge is detected. Otherwise returns 0. */ unsigned long read_pulse(int pin) { static unsigned long rising_time; // time of the rising edge static int last_state; // previous pin state int state = digitalRead(pin); // current pin state unsigned long pulse_length = 0; // default return value // On rising edge: record current time. if (last_state == LOW && state == HIGH) { rising_time = micros(); } // On falling edge: report pulse length. if (last_state == HIGH && state == LOW) { unsigned long falling_time = micros(); pulse_length = falling_time - rising_time; } last_state = state; return pulse_length; }
このread_pulse関数を用いると、入力があったタイミングでそれまでの時間を返却してくれるので、ループ処理で毎回呼び出して、0より大きい数値が返されたときに距離計算をします。
例えば、下記のような形で、他の処理をブロックせずに常に距離計測を行う実装ができます。Trigからは400msごとに超音波を発生させ、Echoからの入力値読み取りは100μsごとに行う形にしています。
long nextTrigPulse = 0;
long currentMicros = 0;
void loop() { int duration = read_pulse(ECHO); if(duration > 0){ int distance = (duration / 2) * speedOfSound * 100 / 1000000; Serial.print("Distance:"); Serial.print(distance); Serial.println(" cm"); }
currentMicros = micros();
// 一定時間ごとに超音波センサのTrigをコールする
if(currentMicros > nextTrigPulse){
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite( TRIG, LOW );
nextTrigPulse = currentMicros + 400000;
} // 何かしらの定期処理 delayMicroseconds(100); }
注意点
この方法のデメリットはread_pulseを呼び出す間隔が長くなってしまうと、精度が下がってしまいます。
例えば、1msごとにread_pulseを呼び出すとすると、1ms以内に超音波が反射してきても、次の1msまで気づくことはできません。音速は340.29m/sなので、1msで34cm進みますので、往復34cm = 約17cmの誤差が生じることになります。
正確性の方が重要な場合はpulseIn関数を使うべきかと思われます。