Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
超音波距離センサモジュールの実験

概要

HC-SR04という超音波距離センサモジュールを使った実験です。

超音波を送信してから、測定対象に反射して、センサーに戻ってくるまでの時間を測定することにより距離がわかるようになっています。

超音波を使って距離を測るセンサです。超音波なので温度によって速度が変わるため、温湿度センサ(HDC1000)も合わせて利用しました。

データシート

HC-SR04のデータシートを参照して、どのようなものかを調べました。

概要

HC-SR04の仕様の概要は以下の通りです。

項目 内容
電源電圧(Vdd) 5V
電流 15mA
測定範囲) 2cm~4m

HC-SR04は4ピンのデバイスです。電源以外に、トリガーピンとエコーピンがあります。トリガーピンは、測定を開始するためのピン、エコーピンは測定結果を出力するピンです。

インタフェース

測定開始

トリガーピンに10μ秒のパルスを与えると、デバイスが超音波の信号を8回送信します。

結果取得

測定開始後、測定対象との距離に応じた時間だけ、エコーピンがHIGHになります。

エコーピンがHIGHになっている時間が、超音波が送信されてから、測定対象に反射して、センサーに戻ってくるまでの時間です。

距離計算

エコーピンがHIGHになっている時間から、測定対象までの距離を計算します。

音の速度は、Wikipediaによると (331.5+0.61t) mとのことなので、その通りに計算します。音は、センサモジュールから送信されて、測定対象に反射して、センサモジュールに戻ってくるので、計算した距離の半分が、測定対象との距離になります。

エコーピンがHIGHになっている時間をTh(μ秒)、温度をtとすると、距離は以下の式で表すことができます。最後に1000000で割っているのは、Thの単位がマイクロ秒だからです。

距離[m] = Th / (331.5 + 0.61 t) / 2 / 1000000

距離[cm] = Th / (33150 + 61t) / 2 / 1000000

実験

HC-SR04のほかに、温度センサとして、HDC1000を接続しました。写真左がHC-SR04、右がHDC1000です。

HC-SR04とArduinoとの接続

HC-SR04とArduinoとを以下のように接続します。TrigとEchoに接続するピンは、スケッチの最初のほうで定義しています。

HDC1000のピン Arduinoのピン番号
VCC 5V
Trig(トリガー) 2
Echo(エコー) 3
GND GND

HDC1000とArduinoとの接続

HDC1000とArduinoとを以下のように接続します。

HDC1000のピン Arduinoのピン番号
+V 5V
SDA SDA(A4)
SCL SCL(A5)
RDY 4
GND GND

Arduinoプログラミング

距離を測定するため、Arduinoのシリアルモニタから1文字送信すると、測定を開始するようにしました。

メインのスケッチ以外に、HDC1000用のスケッチが必要です。

スケッチ

hcsr04.ino

#include <Wire.h>
#include "HDC1000Class.h"

#define HCSR04_TRIGGER_PIN 2
#define HCSR04_ECHO_PIN    3
#define HDC1000_RDY_PIN    4

HDC1000Class hdc1000(HDC1000_RDY_PIN);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(HCSR04_TRIGGER_PIN, OUTPUT);
  pinMode(HCSR04_ECHO_PIN, INPUT);
  hdc1000.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  int pulseWidth;
  double distance;
  float temperature, humidity;
  
  if (Serial.available() > 0) {
    Serial.read();
    
    // measure temperature
    hdc1000.getTemeratureAndHumidity(&temperature, &humidity);
    
    // trigger
    digitalWrite(HCSR04_TRIGGER_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(HCSR04_TRIGGER_PIN, LOW);
    
    // measure pulse width
    pulseWidth = pulseIn(HCSR04_ECHO_PIN, HIGH);
    distance = pulseWidth * (33150.0 + 61.0 * temperature) / 2000000.0;
    
    Serial.print("temperature = ");
    Serial.print(temperature);
    Serial.print(", pulseWidth = ");
    Serial.print(pulseWidth);
    Serial.print(", distance = ");
    Serial.print(distance);
    Serial.print("cm\n");
    delay(1000);
  }
}

HDC1000Class.h

#ifndef HDC1000_CLASS_H
#define HDC1000_CLASS_H

#define HDC1000_ADDRESS 0x40 /* or 0b1000000 */

#define HDC1000_TEMPERATURE_POINTER     0x00
#define HDC1000_HUMIDITY_POINTER        0x01
#define HDC1000_CONFIGURATION_POINTER   0x02
#define HDC1000_SERIAL_ID1_POINTER      0xfb
#define HDC1000_SERIAL_ID2_POINTER      0xfc
#define HDC1000_SERIAL_ID3_POINTER      0xfd
#define HDC1000_MANUFACTURER_ID_POINTER 0xfe

#define HDC1000_CONFIGURE_MSB 0x10 /* Get both temperature and humidity */
#define HDC1000_CONFIGURE_LSB 0x00 /* 14 bit resolution */

class HDC1000Class {
  public:
    HDC1000Class(int rdyPin);
    void begin();
    int getManufacturerId();
    void getTemeratureAndHumidity(float *temperature, float *humidity);
  private:
    int useRdyPin;
    int rdyPin;
};

#endif /* HDC1000_CLASS_H */

HDC1000Class.cpp

#include "HDC1000Class.h"
#include "Arduino.h"
#include "Wire.h"

HDC1000Class::HDC1000Class(int rdyPin) {
  delayMicroseconds(15000);
  if (rdyPin < 0) {
    useRdyPin = false;
  } else {
    useRdyPin = true;
    this->rdyPin = rdyPin;
    pinMode(rdyPin, INPUT);
  }
}

void HDC1000Class::begin() {
  Wire.begin();
  
  Wire.beginTransmission(HDC1000_ADDRESS);
  Wire.write(HDC1000_CONFIGURATION_POINTER);
  Wire.write(HDC1000_CONFIGURE_MSB);
  Wire.write(HDC1000_CONFIGURE_LSB);
  Wire.endTransmission();
}

int HDC1000Class::getManufacturerId() {
  int manufacturerId;

  Wire.beginTransmission(HDC1000_ADDRESS);
  Wire.write(HDC1000_MANUFACTURER_ID_POINTER);
  Wire.endTransmission();

  Wire.requestFrom(HDC1000_ADDRESS, 2);
  while (Wire.available() < 2) {
    ;
  }

  manufacturerId = Wire.read() << 8;
  manufacturerId |= Wire.read();

  return manufacturerId;
}


void HDC1000Class::getTemeratureAndHumidity(float *temperature, float *humidity) {
  int tData, hData;

  Wire.beginTransmission(HDC1000_ADDRESS);
  Wire.write(HDC1000_TEMPERATURE_POINTER);
  Wire.endTransmission();

  if (useRdyPin) {
    while (digitalRead(rdyPin) == HIGH) {
      ;
    }
  } else {
    delay(15);
  }

  Wire.requestFrom(HDC1000_ADDRESS, 4);
  while (Wire.available() < 4) {
    ;
  }

  tData = Wire.read() << 8;
  tData |= Wire.read();

  hData = Wire.read() << 8;
  hData |= Wire.read();

  *temperature = tData / 65536.0 * 165.0 - 40.0;
  *humidity = hData / 65536.0 * 100.0;
}

実行の様子

その他

クラス変数をグローバル変数として定義したとき(下の8行目)、コンストラクタの中でdelay()関数を利用すると、プログラムが動きませんでした。

#include <Wire.h>
#include "HDC1000Class.h"

#define HCSR04_TRIGGER_PIN 2
#define HCSR04_ECHO_PIN    3
#define HDC1000_RDY_PIN    4

HDC1000Class hdc1000(HDC1000_RDY_PIN);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(HCSR04_TRIGGER_PIN, OUTPUT);
  pinMode(HCSR04_ECHO_PIN, INPUT);
  hdc1000.begin();
}

ウェブで調べてみると、以下の理由のようです。

  • delay()は、中でmicros()を利用している。
  • micros()は、中でタイマを利用している。
  • グローバル変数としてクラス変数を定義したときのコンストラクタは、タイマの初期設定を行う前に起動される。

delay()の代わりにdelayMicroseconds()を使えるのであれば、delayMicroseconds()を使えば問題ないとのことです。delayMicroseconds()は、ビジーループです。

Arduino dueでは問題は出なかったはずですが。

バージョン

Arduino 1.6.0/Arduino Uno



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2015 garretlab all rights reserved.
inserted by FC2 system