The heart pulse detector which I constructed on a breadboard has been working so well that I decided to create a sketch on arduino uno to improve it. The code is a combination of smoothing and calibration from the arduino main site. By combining those two and implementing a rising edge detector along with two more concurrent plot traces, the detector can show and count every pulse quite clearly without distortion, not only at finger tips but at many other points inc. ear lobe, nose, lower finger, wrist etc. The detector itself is simply an ldr- and red led (which I found to work a whole lot better than a matching pair of IR trans/receiver.
The circuit mainly consists of a quad amp with two parts used as amplifier- low pass active filter. I used a third part as a final volts boost via a pot which makes it so adjustably strong the fourth part is unnecessary.
Arduino calibrates the analog input down to 0 and 255 by examining the sensor strength during the first ten seconds (or as long as reqd) and then makes a moving average smoothing of as many or few of those adjusted input values as you wish...I set it at 5. The auto-adjusting vertical scale of the arduino plot was jumpy and annoying at first so I sent the results in to processing to use the graph there instead. After some thought I went back to Arduino and created a second concurrent linear horizontal plot trace somewhat higher than 255 and that stopped any auto jumping adjustment of the vertical scale. I then put the sketch within a 10second millis time frame and told it to print out the pulse per minute at the end of 10 secs. .....seen on the arduino monitor. ...I used float instead of int in an effort to get more precision on the counts but I don't think that it improves over approx ± 3 pulses /min on 60 counts.....not great precision, but a person's pulse can vary considerably at each new test, so I am happy with that.
You might want to use that sketch that I made here below to use a a low frequency counter with arduino instead of a scope.....the precision would probably be better for higher frequencies than for my 1/sec pulse but remember to adjust your low pass filter components to suit your own requirements before sending it to the arduino
Here is the entire sketch......let me know if you have any questions about it......just apply any pulse input to A0 of the arduino and upload the sketch to plot in real time....you could copy/paste it in to a text file from here, then paste it in to a new clear arduino sketch at leisure. I'll put the bread board circuit up here if you like, but many can be found on you tube...just remember to ignore IR and simply tape an led alongside an LDR.....they detect the change in light of a skin pulse incredibly easily. I have made notes alongside many of the sketch code lines for you.
// 2ctiby 22 May 2016
const int numReadings = 5; // smoothing amount of values chosen here eg 1 to 15
float sensorValue = 0.00; // the sensor value //floats used....no better
float sensorMin = 1023.00; // minimum sensor value
float sensorMax = 0.00; // maximum sensor value
float sensorValuex = 0.00;
float counterpulse = 0.00;
float readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
float total = 0.00; // the running total
float average = 0.00; // the average
float inputPin = A0;
int array[2];
int count = 0;
void setup() {
// initialize serial communication with computer:
Serial.begin(9600);
// initialize all the readings to 0:
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
}
void loop() {
while (millis() <14000) {
sensorValue = analogRead(A0);
// record the maximum sensor value
if (sensorValue > sensorMax) {
sensorMax = sensorValue;
}
// record the minimum sensor value
if (sensorValue < sensorMin) {
sensorMin = sensorValue;
}
// apply the calibration to the sensor reading sensorValue now calibrated for each new initiation with zero base
sensorValue = map(sensorValue, sensorMin, sensorMax, 0.00, 255.00); // standardises the chosen up/down cutoff in count
// in case the sensor value is outside the range seen during calibration
sensorValue = constrain(sensorValue, 0.00, 255.00);
sensorValuex = sensorValue; //renamed here to be the final reading at this point...cp was altered before
// while (millis() <14000) {
// subtract the last reading:
total = total - readings[readIndex]; //get rid of the entry which is at this current index ...off total
// read from the smoothed calibrated sensor:
readings[readIndex] = sensorValuex; // ... put in a new entry
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1; // move to next index
// if we're at the end of the array...
if (readIndex >= numReadings) {
// ...wrap around to the beginning:
readIndex = 0;
}
// calculate the average:
average = total / numReadings; // average is the ongoing smoothed sensorValue looped now
if ((millis() >= 3000) && (millis() <= 13000) ) {
Serial.print(500); // just to indicate the 10 sec stretch
Serial.print(" ");
Serial.print(5); // the chosen cut off up/down point
Serial.print(" ");
Serial.print(average);
Serial.println(" ");
delay(30); // delay in between reads for stability
for (int i=0; i< 2; i++){
array[i] = average;
// delay(2);
if ((array[0] <= 5.00) && (array[1] > 5.00)) { // NB 100 default ....going upwards here as on leading edge
count = count + 1;
}
if (millis() >= 13000) {
Serial.println(analogRead(A0)); // this ends a line at the last readout point of the 10 sec stretch
Serial.println(" ");
Serial.print("pulse "); // rem.... pulse/10 = frequency Hz if reqd for other pulse analyais
Serial.print(count * 6);
Serial.println(" plus or minus 3 beats per min ref: 2ctiby");
delay(900000);
}
}
}
}
}
|