Part of my project is to build heart rate monitor from Nano and a PulseSensor. I am having trouble working with the Fast Fourier Transform part of the code.

This code here is from a library (arduinofft) that can be found in the arduino IDE. When testing the arduino the serial printer returns values of about 51 bpm, which is far to low, compared to other devices of about 70bpm.

What i have done:

  • changed the sampling frequency to 5.6Hz to get the range of 0 to 2.8Hz range for the hate rate
  • edited the library to only take the last 25 to 128 samples (only when computing the max peak), to off-set the DC. Previously getting readings of 5 BPM

What i don't understand:

  • the sampling period, if the samples are taken and put into an array every second what does the period do.

  • Also if samples are taken every second is this enough to get a good reading on a heart rate shouldn't the sample be maybe of 15-30 seconds?

  • If this is so, should i put another array at the bottom. To capture the results every second upto 15 or 30, print the average then first in first out the array of new values?

#include "arduinoFFT.h"

arduinoFFT FFT = arduinoFFT(); /* Create FFT object */

#define CHANNEL A0
const uint16_t samples = 128; //This value MUST ALWAYS be a power of 2
const double samplingFrequency = 5.6; //Hz, must be less than 10000 due to ADC

unsigned int sampling_period_us;
unsigned long microseconds;

double vReal[samples];
double vImag[samples];

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03

void setup()
  sampling_period_us = round(1000000*(1/samplingFrequency));

void loop()
  for(int i=0; i<samples; i++)
      microseconds = micros();    //Overflows after around 70 minutes!
      vReal[i] = analogRead(CHANNEL);
      vImag[i] = 0;
      while(micros() < (microseconds + sampling_period_us)){    

  FFT.Windowing(vReal, samples, FFT_WIN_TYP_RECTANGLE, FFT_FORWARD);    /* Weigh data */
  FFT.Compute(vReal, vImag, samples, FFT_FORWARD); /* Compute FFT */
  FFT.ComplexToMagnitude(vReal, vImag, samples); /* Compute magnitudes */ 
  double x = FFT.MajorPeak(vReal, samples, samplingFrequency); /* Compute largest peak*/
  double y = round((60/(1/x)));
  Serial.println(y);  //print bpm 
  delay(2000); /* Repeat after delay */

Thanks for any help

  • $\begingroup$ What is exactly the signal you are working on ? An ECG ? $\endgroup$
    – MaximGi
    Mar 30 '19 at 11:52

1) The sampling period ($T_{s}$) is the time between each sample of the observed analog signal that is captured and stored in 'Vreal'

2) The total observation time ($T_{obs}$) is then equal to $N T{s}$ with N being the number of samples you captured

Very roughly, In order to properly monitor your signal, you need $T_s$ to be at the very least 2 times lower than the fastest observed phenomenon. In other words, you need the sampling frequency ($F_s$) to be 2 times greater than the signal's maximum frequency (see https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem).

Also, you need $T_{obs}$ to be great enough to capture enough heartbeats to the spectral analysis done to determine the heart pulse rate actually makes sense.

What you need to know then, is :

1) How much heartbeats you need to capture so the heartbeat can be properly determined. This will be answered by people working in the field of medicine more than people from signal processing

2) How is a heart beat represented in terms of spectral content, and how large is its spectral bandwidth so you can set up your sampling frequency high enough to be able to capture them without missing critical components

  • $\begingroup$ Thanks for your help, I am getting good results so I think it's all good. Cheers $\endgroup$
    – user55291
    Apr 6 '19 at 20:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.