Measuring heart rate with a piezoelectric vibration sensor

While trying to create a circuit that detects whether water is flowing through a pipe by measuring the vibration with a piezoelectric sensor, just to see what happens I taped the sensor around my finger and – to my surprise – got values that were a very noise-free representation of my heart rate!

Piezo sensor taped to finger
Piezo sensor taped to finger
Analog values from a piezoelectric sensor taped to my finger.
Analog values from a piezoelectric sensor taped to my finger.

This is even easier than using LEDs as it only requires a piezoelectric sensor and an Arduino. (and a piece of tape)
The sensor I used is a DFRobot Piezo Disc Vibration Sensor Module.

void setup() {
  Serial.begin(57600); 
}

void loop() {
  int avg = 0;
  for(int i=0;i<64;i++){
    avg+=analogRead(A2);
  }
  Serial.println(avg/64,DEC);
  delay(5);
}

When defining an arbitrary threshold (e.g. half of the maximum measured value), the rising edge of the signal will pass the threshold once per heartbeat, making measuring it as simple as measuring the time between two successive beats. For less jitter, I chose to calculate the heart rate using the average of the last 16 time differences between the beats.

Here’s a quick and dirty code that calculates the heart rate and outputs the average heart rate over the last 16 beats at every beat:

int threshold = 60;
int oldvalue = 0;
int newvalue = 0;
unsigned long oldmillis = 0;
unsigned long newmillis = 0;
int cnt = 0;
int timings[16];

void setup() {
  Serial.begin(57600); 
}

void loop() {
  oldvalue = newvalue;
  newvalue = 0;
  for(int i=0; i<64; i++){ // Average over 16 measurements
    newvalue += analogRead(A2);
  }
  newvalue = newvalue/64;
  // find triggering edge
  if(oldvalue<threshold && newvalue>=threshold){ 
    oldmillis = newmillis;
    newmillis = millis();
    // fill in the current time difference in ringbuffer
    timings[cnt%16]= (int)(newmillis-oldmillis); 
    int totalmillis = 0;
    // calculate average of the last 16 time differences
    for(int i=0;i<16;i++){
      totalmillis += timings[i];
    }
    // calculate heart rate
    int heartrate = 60000/(totalmillis/16);
    Serial.println(heartrate,DEC);
    cnt++;
  }
  delay(5);
}

If you would like to try this at home, just connect the analog output of the sensor to A2 (or change the code) and connect the 5V and GND lines of the sensor.

35 thoughts on “Measuring heart rate with a piezoelectric vibration sensor”

  1. Hey man, nice work!
    I’m doing an “health wristband” and I need to catch heart beats. Could you test around the wrist and show the results? This method needs to be pressured the sensor on the skin? Or just the touch could work?
    Thanks in advance.

  2. Wau – this is a great idea. If you the first one you should claim a patent. In our institute we have tried to find a simple method to measure the heart frequency but nothing worked very well, e.g. led-based methods are very noisy and need to much energy. Your method worked like a charm (I have just tried it) and needs no energy. The big player in fitness tracking will use it.

    Congratulation Felix

  3. Very nice idea, I’m surprised by its simplicity, thank you 🙂 One suggestion though, maybe you better put a protection diode between the analog in and vdd, the piezo can easily generate tens of volts if unadvertently slammed and fry your analog in.

  4. Is it me or does the serial monitor give me lot of gibberish symbols?
    Wiring is only one in the A2 and one in the ground, not?

  5. Keep in mind that the serial connection is set to 57600 baud, so you also have to set it to that in the serial monitor!

  6. Hey,

    In one statement, patent this.

    Since old electronics mags I didn’t see anything as useful.

    Nicely done, thanks for sharing.

  7. He did that to get the average of 64 ADC measurements. This adds to resolution and lowers noise. If you modify this so that a complete period of 50Hz hum is covered (maybe using millis() or delay_us()) noise (50Hz become even lower.

  8. Hi, can you please eplain how to display the output in java data log?
    i can see the output in the serial monitor only !
    Thank you

  9. I try to modify your code to transfer heartbeat to my android app but am kind of confuse. Please can anybody help out.
    int threshold = 60;
    int oldvalue = 0;
    int newvalue = 0;
    unsigned long oldmillis = 0;
    unsigned long newmillis = 0;
    int cnt = 0;
    int timings[16];
    void setup() {
    Serial.begin(57600);

    }

    void loop() {
    if(Serial.available()>0){
    char re = Serial.read();
    switch(re){
    case ‘E’:
    start();
    break;
    }

    }
    }

    void start(){
    while(1){
    oldvalue = newvalue;
    newvalue = 0;
    for(int i=0; i0){
    if (Serial.read()==’Q’) return;
    }
    }

    int floatMap(int newvalue){
    oldvalue = newvalue;
    newvalue = newvalue/64;
    // find triggering edge
    if(oldvalue=threshold){
    oldmillis = newmillis;
    newmillis = millis();
    // fill in the current time difference in ringbuffer
    timings[cnt%16]= (int)(newmillis-oldmillis);
    int totalmillis = 0;
    // calculate average of the last 16 time differences
    for(int i=0;i<16;i++){
    totalmillis += timings[i];
    }
    // calculate heart rate
    int heartrate = 60000/(totalmillis/16);
    cnt++;
    }
    return heartrate;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *