Implementing timer interrupts for pulse sensor [SOLVED]

I’m trying to use code from the pulse sensor (http://pulsesensor.com) with the spark, but need to use timer interrupts. How would I convert the following code to work with the core?

void interruptSetup(){
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON’T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}

// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise

// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}

if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave

// NOW IT’S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse

if(firstBeat){ // if it’s the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
return; // IBI value is unreliable so discard it
}
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}

// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable

for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}

rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that’s BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}

if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}

if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = true; // when we get the heartbeat back
}

sei(); // enable interrupts when youre done!
}//

Larum, can you please edit your post and select the entire code you posted and mark it as Preformated using the </> item on the edit bar.

The ISR you posted is quite long and not Spark friendly I suspect. There are two ways to do this code. The first is to create a 2ms timer in loop() using elapsedMillis() for example and move the code there. You can see an example of this type of sampling in the water flow sensor topic. I can help you with this method if you should so need it.

The second is to use a hardware timer which is more complicated. You can look at the timer interrupts topic for more information. However, I still believe the ISR code you posted is too long and it locks out ALL other interrupts while running, which is not a good idea on the Spark. :smile:

here is the Arduino walkthrough from the Pulse Sensor site:
http://pulsesensor.myshopify.com/pages/pulse-sensor-amped-arduino-v1dot1

Do you think it cannot be implemented with the core?

larum, I realize I have not answered you on this topic. I am working on an interval timer pool library which will give you access to a timer the same way it is done in the arduino code, at least functionally. I am not sure when it will be done as I just got to it. Have you made any progress?

1 Like

I just ordered one of these sensors, so I’ll be working on getting it to work with the spark core as well.

@larum were you able to get it to work at all?

Unfortunately no. Ended up using a Teensy. When you succeed, I’d love to see the code!

1 Like

Hypnopompia, larum - I believe I can use my IntervalTimer library to do the hardware timing and port this library. Let me work on that and get back to you guys for testing. :smile:

3 Likes

Freaking sweet! Thanks @peekay123!

Seriouly! I think @peekay123's new nickname should be "The Porter". I'd drink to that!

4 Likes

OK boys, here it is. I have no way of testing it so I need you guys to give it a go. The code is HERE on my github. Please review the instructions in the README files for the pins used by the Spark version. Please let me know how it goes!

Hey, better than being call “The Porker” I guess :open_mouth:

2 Likes

@peekay123 Once I get my part (it shipped already) I’ll test it out. I’ll also be bringing it with me next week to let you play with. :smile:

Looks like I owe you a beer!

1 Like

@peekay123 I was able to get this compiled and uploaded. The interrupt is firing often, and it’s getting sensor data from the analog input (I can see the number fluctuate with my heartbeat) but for some reason the library isn’t detecting a heartbeat within those numbers. I’m wondering if it has something to do with the timing in the pulseISR() function (it detects a heartbeat based on sensor values and time to look for spikes).

I also wonder if because I’m running it at 3V instead of 5V, if the threshold for the sensor value to detect a heartbeat is skewed.

Hypnopompia, you may be correct about the timing and voltage. Let me review the code and get back to you.

UPDATE: I think I found the problem. The default values for thresh, P, T and amp are all set for the 10bit ADC of the Arduino. The Spark has a 12bit ADC. So the correct values (in interrupt.h) should be as follows:

thresh = 2048
P = 2048
T = 2048
amp = 410

I have updated the values in the file on my github. Give that a shot and let me know how it goes. :smile:

1 Like

@peekay123 It works! It turns out that I do have a pulse! Thanks so much for your help!

1 Like

Dude, I gotta get me one of those. So happy it is now working for you! And at 100 BPM, I can see you’re quite excited also :wink:

2 Likes

Hi there,
Great that you got the code to work, but for some reason I cannot get your code from Github to compile:
this is the end of the error:

/spark/compile_server/shared/workspace/worker_2/core-firmware/build/../src/stm32_it.cpp:541: multiple definition of `TIM4_IRQHandler'
/SparkIntervalTimer.o:/spark/compile_server/shared/workspace/worker_2/core-firmware/build//SparkIntervalTimer.cpp:56: first defined here
collect2: error: ld returned 1 exit status
make: *** [53dfd708873e8addaab36814ce9dfebfa4460f542b67bf5baf0842dea971.elf] Error 1

Any ideas how I might resolve this? Thanks!

@larum, some fixes were made recently that fixed that specific issue. You may want to pull the latest code and try again :smile:

1 Like

And I just issued a pull request for some changes to get it to compile in the web ide and work with the processing sketch. @peekay123 merged in the pull request so fast, I didn’t have time to post an update! So if anyone had troubles getting it to compile, grab the .ino file again.

Aren’t pull requests fun?

2 Likes

@Hypnopompia, you are the man! Thanks for those fixes :smile:

2 Likes