心拍センサを Seeeduino で使ってみる(Groveコネクタ接続 & ポーリング版)

 前回書いた 心拍センサを Seeeduino で使ってみる 記事では、公式ページで紹介されているサンプルコードをベースに心拍センサでの心拍の検知をインタラプトで受け取るため、 Grove コネクタにジャンパケーブルを挿して D2 ピンを使っていました。

f:id:akanuma-hiroaki:20180721180712j:plain

 ですがこれだと折角 Grove コネクタがついているのにそのメリットを活かすことができません。インタラプトで受け取ることありきで考えていたので仕方ないかなと思っていたのですが、前回の記事公開後に下記のようなコメントをいただきました。

f:id:akanuma-hiroaki:20180721181100p:plain

 そこで今回は Grove コネクタで I2C に接続して、ポーリングで処理する版を試してみました。

回路構成

 今回の回路は Grove コネクタを Seeduino の Grove コネクタの I2C に接続するだけなのですっきりシンプル構成です。

f:id:akanuma-hiroaki:20180721224724j:plain

f:id:akanuma-hiroaki:20180721224745j:plain

ファームウェア

 前回のコードをベースにいくつか変更を加えました。

 前回は setup()attachInterrupt() を使って割り込みを受け付ける設定をしていましたが、その部分を削除し、代わりに pinMode() で A5 ピンからセンサの入力を読み取るための設定をしています。

 また、 前回は loop() では何もしていませんでしたが、前回割り込み検知時に実行していた処理を 100ms の delay で繰り返し実行するようにし、その中で A5 ピンの入力値をチェックするようにしています。

 A5 ピンからの入力値が 0 の場合もしくは 1 の状態が続いているときは何もせずに return し、0 から 1 に変化した時には前回同様の後続の処理を行うことで、インタラプト使用時と同じ挙動になるようにしてみました。もし 100ms 未満の間隔で心拍を検知してまた 0 に戻るようなことがあると検知できないのですが、最初に入力値が 1 かどうかだけをチェックしてやってみたところ、 100ms の delay で 1 の状態が複数回続いていたので、今回はこのやり方でやってみています。

unsigned char counter;
unsigned long temp[21];
unsigned long sub;
bool data_effect = true;
unsigned int heart_rate;
int hrm_output;
int hrm_output_tmp;

const int max_heartpluse_duty = 2000;

void setup()
{
    Serial.begin(9600);
    Serial.println("Please ready your chest belt.");
    delay(5000);
    arrayInit();
    Serial.println("Heart rate test begin.");
    pinMode(A5, INPUT);
}

void loop()
{
  interrupt();
  delay(100);
}

void sum()
{
    if(data_effect)
    {
      heart_rate = 1200000 / (temp[20] - temp[0]); 
      Serial.print("Heart_rate_is:\t");
      Serial.println(heart_rate);
    }
    data_effect = 1;
}

void interrupt()
{
    hrm_output_tmp = digitalRead(A5);

    if(hrm_output_tmp == 0)
    {
      hrm_output = 0;
      return;
    }

    if(hrm_output_tmp == hrm_output)
    {
      return;
    }

    hrm_output = hrm_output_tmp;
    temp[counter] = millis();
    Serial.println(counter,DEC);
    Serial.println(temp[counter]);
    switch(counter)
    {
        case 0:
            sub = temp[counter] - temp[20];
            Serial.println(sub);
            break;
        default:
            sub = temp[counter] - temp[counter - 1];
            Serial.println(sub);
            break;
    }

    if(sub > max_heartpluse_duty)
    {
        data_effect = 0;
        counter = 0;
        Serial.println("Heart rate measure error,test will restart!" );
        arrayInit();
    }

    if (counter == 20 && data_effect)
    {
        counter = 0;
        sum();
    }
    else if(counter != 20 && data_effect)
        counter++;
    else 
    {
        counter = 0;
        data_effect = 1;
    }

}

void arrayInit()
{
    for(unsigned char i = 0; i < 20; i++)
    {
        temp[i] = 0;
    }
    temp[20] = millis();
}

 これを実行すると前回同様に Serial モニタに下記のように出力されます。前回のコードで実行した時と大体同じような結果になったので、ひとまず計測できているようです。

0
1620177
1001
1
1621079
902
2
1622079
1000
3
1623079
1000
4
1623981
902
5
1624881
900
6
1625882
1001
7
1626782
900
8
1627583
801
9
1628583
1000
10
1629485
902
11
1630285
800
12
1631086
801
13
1631987
901
14
1632988
1001
15
1633889
901
16
1634789
900
17
1635690
901
18
1636590
900
19
1637491
901
20
1638292
801
Heart_rate_is:  66

まとめ

 センサからのデータの発生タイミングに依存する処理を行う場合には、インタラプト処理の方がポーリング間隔等を気にする必要もないので向いていると思いますが、一方でプロトタイピングを行う場合は Grove コネクタ等を活用することで半田付けや複雑な配線をしなくて済むのはメリットが大きいですね。特に仕事でプロトタイピングをする場合はいかに機材を少なくして半田付けもなしで試せるかというのは重要なので、やりたい内容によって使い分けるのが重要に思います。

 @maris_HY さん、コメントありがとうございました!