DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

超音波センサーモジュールをPICを使って人感センサーにする 備忘録

超音波センサモジュール「HC-SR04」が手に入ったので使ってみました。

 

しかし、このモジュール不具合品が含まれることで有名なもので手元にあるものを調べてみました。

mag.switch-science.com

 

あらら、見事に不具合品に合致します。

テストしても確かにモジュールの電源を入れ直さないと連続して距離が測れないので、今回使おうとしている用途では全く使い物になりません。

大した消費電力ではないのでマイコンのピンから直接電源が取れないかと試しましたが駄目でした。

 

不具合の修正方法が投稿されています。

<参考>

超音波距離センサーモジュール「HC-SR04」回路不具合の修正

 

参考サイトに習ってR1の抵抗を外し、

 

外した抵抗を再利用してGNDへプルダウンしました。

結果、手元のモジュールではこの方法では駄目でした。

 

仕方がないので別のモジュールを使うことにしました。

こちらは不具合無しのものです。

現行で売られているものはほとんど不具合無しのモデルのようです(当たり前か)。

 

 

 

使い方は簡単です。

5V電源とGNDを繋いで起動。

Trig端子を10マイクロ秒だけHIレベルにします。

するとモジュールから超音波が出て、反射した音波が帰ってくる時間に応じてEcho端子がHIレベルになります。

Echo端子がHIレベルになっている時間をマイコンで計測して結果を文字に出したりするだけです。

Web上にサンプルも多く出ているので特にここでは記述しなくても良いでしょう。

 

今回はこのモジュールを激安マイコン「PIC10F222」で制御して人感センサーとする目論見です。

<過去記事参考>

dreamerdream.hateblo.jp

 

 

まずはPICに移植できるコードをArduinoで書いてみます。

ArduinoにはEcho端子のHIレベルを計測するのに便利なpulseIn()関数が用意されていますが、PICには無いので自前で用意しないといけません。

PIC10Fシリーズは小規模なマイコンなのでArduinoのように多大なメモリ領域が使えません。それにスタック領域も2つ、つまり関数の呼び出せる層が2つだけなのでロジックを組むにも少し工夫が必要です。変数も0~255までのunsigned charを出来るだけ使いまわして省メモリ化します。

その代わり超省エネで回路が簡単、単純な動作をさせるにはオススメのマイコンです。

 

ArduinoはUSBで簡単に書き換えられるのでPICに比べて動作確認はめちゃめちゃ楽ですのでPIC用コードを実際に書く前のロジックの確認用としてかなり使えます。

クソコードですがArduino用コード

unsigned char TRIG = 3;
unsigned char ECHO = 2;
unsigned char LED = 4;
unsigned int cnt_avl = 0;

void setup() {
  Serial.begin( 9600 );

  pinMode(ECHO, INPUT );
  pinMode(TRIG, OUTPUT );
  pinMode(LED, OUTPUT );
 

}

unsigned char getCnt(){

  digitalWrite(TRIG, LOW); 
  delayMicroseconds(2);
  digitalWrite( TRIG, HIGH );
  delayMicroseconds( 10 ); 
  digitalWrite( TRIG, LOW );
  unsigned char cnt = 0;


  //cnt = pulseIn( ECHO, HIGH ); // 往復にかかった時間が返却される[マイクロ秒]
  
  while( digitalRead( ECHO ) == 0 ){
    delayMicroseconds( 10 );
    cnt++;
    if( 254 < cnt){
      break;
    }
  }
  cnt = 0;
  while( digitalRead( ECHO ) == 1 ){
    delayMicroseconds( 40 );
    cnt++;
    if( 254 < cnt){
      delay(20);
      break;
    }
  }
  
  return cnt;
}

void loop() {
  unsigned char scale = getCnt();

  if( scale < cnt_avl - cnt_avl/10 ){
    digitalWrite( LED, HIGH );
    Serial.println("Active!!");
    delay( 10000 );
    digitalWrite( LED, LOW );
  }

  if( scale != 0 && scale != 255 ){
    cnt_avl = ( scale + cnt_avl )/2;
  }
  Serial.print("cnt: ");
  Serial.print( cnt_avl );
  Serial.println("cnt");
    
  delay(300);
  
}

 

「0.3秒ごとに計測し、待機状態の1/10以上の反射時間が短くなる変化(物体が近くなったり横切ったりする変化)があれば10秒間スイッチをONにする。」だけの動作です。

 

これを、この回路のPICへ移植します。

 

動作は「センサーが反応すると10秒間GP1とGP2のHIとLOが入れ違う」だけです。

 

やっつけ感満載の基板ですが完成。

当たり前だけどArduinoより遥かにシンプルですね^^vマイコンの動作だけだとPICとパスコンだけで完結しています。こんな単純な回路で充分動くのがPICの良いところ。

 

プログラムはMPLABの出番です。流石に古い…(WindowsXP)

PIC10F222用に移植したコード

#include  <pic.h>

/*****  コンフィギュレーションの設定  ********/
//__CONFIG( OSC_IntRC & CP_OFF & WDT_OFF & MCLRE_OFF );
//オシレータ種類 & コードプロテクト & ウォッチドックタイマ & リセットピン
//内部4MHz
// file:///C:/Program%20Files/Microchip/xc8/v1.30/docs/chips/10f222.html

//MCLRE = ON : ResetPin  OFF: GPIO Pin

#pragma config MCLRE = OFF, WDTE = OFF, IOSCFS = 4MHZ, CP = OFF, MCPU = OFF

#define _XTAL_FREQ 4000000     //CPU4MHz設定delay関数用。
// #define FOSC 4000000L

#define Led GP0
#define Out GP1
#define Trig GP2
#define Echo GP3 // GP3: input only

unsigned char i = 0;
unsigned char cnt_1ms =0;
unsigned char cnt_100ms =0;
unsigned char cnt_10s =0;

unsigned int cnt_avl =0;


/* initial */
void init_com(void){
   /*STATUS
    bit 7:ピン変化によるウェイクアップ(GP0, GP1, GP3) 0:電源投入時リセット 1:ピン変化リセット
    bit 6:Non
    bit 5:Non
    bit 4:タイムアウト0 WDT 1電源投入 
    bit 3:パワーダウン 0sleep命令 1電源投入
    bit 2:ゼロビット 0算術演算結果が0以外 1結果が0 
    bit 1:デジットキャリー・ボロー 0キャリー発生なし、ボロー発生あり 1逆 
    bit 0:キャリー 0 1 
    */
    STATUS =0b00000000;

    /**  OPTION
    bit 7 :ピン変化によるウェイクアップGP0 GP1 GP3 0有効 1無効
    bit 6 :弱プルアップ 0有効 1無効
    bit 5 :タイマ0クロックソース 0内部 1T0CKIピン
    bit 4 :タイマ0ソースエッジ 0 L->H 1 H->L
    bit 3 :ぷリスケーら 0 タイマ0 1 WDT
    bit 2-0 :ぷリスケーらレート選択 0 1
    **/
    OPTION = 0b10000000;

    ADCON0 = 0b00001111; //0 Analog 1 digital

    TRIS = 0b11111000; //出力:0 入力:1  (GP3は入力のみ)
    GPIO = 0b00000010;

    //TRISが未対応らしいのでインラインアセンブラで記述
    //asm("movlw 0x0B");    
    //asm("TRIS 6");
}

/* wait (0 - 255) m secound */
void delay_1ms(unsigned char cnt){
    for(cnt_1ms = 0 ;cnt_1ms < cnt; cnt_1ms++){
        __delay_ms(1);
    }
}

/* wait (0 - 255) *100m secound */
void delay_100ms(unsigned char cnt){
    for(cnt_100ms = 0 ;cnt_100ms < cnt; cnt_100ms++){
        __delay_ms(100);
    }
}

/* wait (0 - 255) *100m secound */
void delay_10s(unsigned char cnt){
    for(cnt_10s = 0 ;cnt_10s < cnt; cnt_10s++){
        delay_100ms( 100 );
    }
}

unsigned char getCnt(){
   Trig = 0;
   __delay_us(2);
   Trig = 1;
   __delay_us(10);
   Trig = 0;

   unsigned char cnt = 0;
   while( Echo == 0 ){
        __delay_us( 10 );
        cnt++;
        if( 254 < cnt){
             break;
        }
    }
    cnt = 0;
    while( Echo == 1 ){
        __delay_us( 40 );
        cnt++;
        if( 254 < cnt){
             break;
        }
    }
    return cnt;
}

/* main */
//Led Out Trig Echo
void main(void){
    init_com();

    while( 1 ){
        Led = 0;
        Out = 1;

        unsigned char scale = getCnt();
        if( scale < cnt_avl - cnt_avl/10){
            Led = 1;
            Out = 0;
            delay_10s(1);
            Led = 0;
            Out = 1;
        }
        if(scale != 0 && scale != 255){
            cnt_avl = ( scale + cnt_avl )/2;
        }


        delay_100ms( 3 );
    }

}

PICはチップ自体は安く済むけれど殆どの便利な関数は自前で用意しないといけませんしコンフィギュレーション設定がマイコン毎や設計回路ごとに変わるのでコードを書くのは実に面倒です。(それでもアセンブラで組むよりは遥かにマシです。)

Arduinoはこのややこしい部分を取っ払ってしまっているのですごく入門しやすい親切設計ですね。

 

とりあえずは狙い通りに動きました。

人が横切ると反応します。

感度調整は状況に応じてコード内の

if( scale < cnt_avl - cnt_avl/10){

の変化率「/10」のところを弄るだけですね。ボリュームで調整できるようにしたらさらに使いやすくなりますが設置箇所は固定なので今回は不要。

 

超音波式の人感センサーは自動ドア等でよく見られる方式で焦電型の人感センサーより屋外での使用には強いはずですが、こういった人感センサー的な設計の超音波モジュールは電子部品として見当たりませんね。何か理由があるのでしょうかね?

 

 

 

実際に使えるかどうかは実運用で試してみることにします。

今回は以上です。

 

 

kampa.me