Hello,
I'm new in arduino programming and to this forum also(this is my first post, please don't mind if you see some mistakes), I'm working on a project of pulse oximetry. I came across some hurdles in the project, one of them is finding the heart rate. From the given picture, I want to find the time duration between two consiquitive maximum points of the green line.
For the context, so far I'm able to determine all these maximum and minimun values for the green waveform.
The algorithm is as follows:
value_ir = A0 (for getting analog voltage throough pin A0)
for(i = 0; i <= 7; i++) // taking sample 8 times
for(j = 0; j<299; j++) // looking for maximum and minimum values over 3 seconds from that waveform (each cycle of for loop takes 10ms to complete hence, 10*300 = 3000ms or 3 seconds)
averaging the maximum values over 8 samples for a stable maximum value.
Example: average(512, 515, 521, 508, 514, 502, 517, 513) = 512
Imagine these values are the 8 peaks of the green waveform, now I want to measure the time duration between the two adjacent values.
The code is as follows:
void loop() {
for (i = 1; i <= samples; i++) // samples = 7
{
maximum_r = 0; // from here
minimum_r = 1023;
maximum_ir = 0;
minimum_ir = 1023; // to here for finding the max and min values
for (j = 0; j < 299; j++) // for 300 times
{
digitalWrite(S1, LOW);
digitalWrite(S0, HIGH);
delay(2);
ir = analogRead(value_ir); // measuring the analog voltage from the green waveform
if (ir > maximum_ir)
{
maximum_ir = ir;
}
if (ir < minimum_ir)
{
minimum_ir = ir;
}
digitalWrite(S0, LOW);
digitalWrite(S1, LOW);
delay(3);
digitalWrite(S1, HIGH);
digitalWrite(S0, LOW);
delay(2);
red = analogRead(value_r); // measuring the analog voltage from yellow waveform
if (red > maximum_r)
{
maximum_r = red;
}
if (red < minimum_r)
{
minimum_r = red;
}
digitalWrite(S0, LOW);
digitalWrite(S1, LOW);
delay(3);
}
max_ir = max_ir + maximum_ir; // from here
min_ir = (min_ir + minimum_ir);
max_r = (max_r + maximum_r);
min_r = (min_r + minimum_r);
max_r = (max_r * 5) / (1023 * samples);
min_r = (min_r * 5) / (1023 * samples);
max_ir = (max_ir * 5) / (1023 * samples);
min_ir = (min_ir * 5) / (1023 * samples); // to here for averaging the max and min
ratio_r = (max_r - min_r) / min_r;
ratio_ir = (max_ir - min_ir) / min_ir;
R = ratio_r / ratio_ir;
sp = ((0.81 - (0.18 * R)) / (0.63 + (0.11 * R))) * 100; // for finding the spo2 value
// then there are some statements of printing the R and SpO2 values
}
Each time you detect a maximum value save the value of millis() as the start time. The next time you detect a maximum value subtract the current value of millis() from the start time to give the interval between them
Thanks for the reply UKHeliBob,
I tried to use millis() function, didn't got any value (inf) there must be some logical error there, could you help me in using the millis() function.
My doubt is where to put the millis() function?
also How will I know at what time instant I got maximum value? because I got to know each value of 8 maximum values after each subloop , so at the end of the sub loop I know what is the mmaximum value.
But in that loop from 0 to 299 at what instant I got the maximum value. How can I know that.
I want to find the time duration between two consiquitive maximum points of the green line
Then you search for one peak in a 3 sec interval.
You'd like to measure all of the green peak values or just the maximum of the peaks during 3sec? Because your code does the latter, as far as I interpret it.
The whole measurement is about 20sec, right? Wouldn't it be better to gather all peaks (and their timestamps) during that time and do anything you'd like with the data when 20sec is over? Or use a 10 element rolling average for their value and period time for example?
Then you did something wrong in the code that you tried but haven't posted
Save the value of millis(), or perhaps micros(), for each value that you read in an array. Once you determine which is the highest value then use the corresponding value in the array of times
Thanks for reply amazed ,
for the carification of the doubt : I'm intersted in finding one peak (which will be maximum over those 3 second), and then I'm taking 8 such 3 second samples which will have their respective maximum peaks over the couse of their 3 seconds of duration. This approach is needed for finding the oxygen level
my doubts is not regarding the maximum of all peaks in the course of 3 seconds. My doubt is : how can I get the values of millis() whenever there is a peak, because I'm getting the maximum value after each 3 seconds of loop, I dont know in those three seconds how many peaks are there and at what milliseconds they are occuring. from 0 to 299, It may occur at 15, 95, 165, 237 so from there I can know also that OK. 95 - 15 = 80 cycles and each cycle is of 10 ms hence 800 ms is the time period that is also different approach.
The simplest approach is by millis()function. If i got to know the milliseconds, I can subtract the adjacent milliseconds value to get heart rate.
Same problem here also, I'm able to know the maximum values over the course of 3 seconds not in between, which consisits of many pulses(maximas). and i want real adjacent maximas for subtracting the millis values.
You are making multiple measurements during a 3 second period.
At the start of a measurement period set the value of your maximum variable to zero, then each time you make a measurement if it is greater that the current value of the maximum variable then save the new value as the maximum and the millis() or micros() value at that time
At the end of the 3 second measuring period you will have the value of the maximum value read during the 3 seconds and the value of millis() at that time
I understood like this:
in the course of 3 seconds, first setting the max_ir = 0 .
now lets say the values of maximas are : 512, 518, 516 and 511 (4 pulses in 3 seconds), and 534, 538, 536 and 535 (4 pulses in next 3 seconds). I can get the millis value of 518 at the end of the first 3 second loop and millis value of 538 of the second 3 second loop easily but,
I want the millis values of 512 and 518 from the first loop only and then the millis values of 516 and 518 and then 511 and 516 and so on.
Thats the thing I want tot ask , how to go and determine the values inside of 3 second loops.
I want the millis values of 512 and 518 from the first loop only and then the millis values of 516 and 518 and then 511 and 516 and so on.
Okay, this is what I've asked in #4. You need to change your current data gather method.
I would go for a measurement array, which consists 10-15 of the previous values of let's say 20ms averaging windows (exact timing has to be fitted to the measurable signal) and search for peaks in this array, and store millis if found one. Period time between peaks can be a rolling average for example.
Is it possible to define a limit, which is the minimum value for a peak? Because you need to ignore the "small" peaks.
Maybe you can use the first 3-5 seconds to check the approx. values of the expected peaks. Or choose a window size that guarantees there will be a peak in it, then if it's too wide you need to deal with double peaks. What is the expected min and max period of the signal? The example is about 700ms.
I thought this through again and experimented a little. You don't necessarily need 10-15 element measurement array. The averaging window time has to be carefully chosen (or adapted at runtime), and only 3 element array will suffice (you should think of it as a circular buffer). After adding the latest window average, you should loop through the array and search for a peak (compare the previous average to the newest and oldest). My code found those "red circled" small peaks also, but after the measurement was over, those could be easily eliminated by their value (small peak values are presumably lower, than the average of all peaks).