Hi,
I am wondering can anyone help me please. I have been trying to put tone / melody to a heart monitor for some time now but I am not getting anywhere.
The data is coming in from the heart sensor but it seems to be jumping all over the place. For ex; when the user is not even moving the pulse is jumping from 50 to 800. If that was real the person would be dead.
So I am trying to put sound onto that its coming up with nothing just crackling noise.
Can anyone help, I am getting really frustrated, and driving myself crazy.
Here is the code: Maybe I am doing something wrong, is there an easier way to attach sound onto the heart pulse monitor.
I have an LED and that is taking in the heart pulse but the sound isnt.
below is the code I have been working on.
/*
>> Pulse Sensor Digital Filter <<
This code is the library prototype for Pulse Sensor by Yury Gitman and Joel Murphy
www.pulsesensor.com
>>> Pulse Sensor purple wire goes to Analog Pin 0 <<<
Pulse Sensor sample aquisition and processing happens in the background via Timer 1 interrupt. 1mS sample rate.
The following variables are automatically updated:
Pulse : boolean that is true when a heartbeat is sensed then false in time with pin13 LED going out.
Signal : int that holds the analog signal data straight from the sensor. updated every 1mS.
HRV : int that holds the time between the last two beats. 1mS resolution.
B : boolean that is made true whenever HRV is updated. User must reset.
BPM : int that holds the heart rate value. derived from averaging HRV every 10 pulses.
QS : boolean that is made true whenever BPM is updated. User must reset.
Scale : int that works abit like gain. use to change the amplitude of the digital filter output. useful range 12<>20 : high<>low default = 12
FSignal : int that holds the output of the digital filter/amplifier. updated every 1mS.
*/
long Hxv[4]; // these arrays are used in the digital filter
long Hyv[4]; // H for highpass, L for lowpass
long Lxv[4];
long Lyv[4];
unsigned long readings; // used to help normalize the signal
unsigned long peakTime; // used to time the start of the heart pulse
unsigned long lastPeakTime = 0;// used to find the time between beats
volatile int Peak; // used to locate the highest point in positive phase of heart beat waveform
int rate; // used to help determine pulse rate //MELODY REVELANT
volatile int BPM; // used to hold the pulse rate //MELODY REVELANT
int offset = 0; // used to normalize the raw data
int sampleCounter; // used to determine pulse timing
int beatCounter = 1; // used to keep track of pulses
volatile int Signal; // holds the incoming raw data
int NSignal; // holds the normalized signal
volatile int FSignal; // holds result of the bandpass filter
volatile int HRV; // holds the time between beats
volatile int Scale = 13; // used to scale the result of the digital filter. range 12<>20 : high<>low amplification
volatile int Fade = 0;
boolean first = true; // reminds us to seed the filter on the first go
volatile boolean Pulse = false; // becomes true when there is a heart pulse //MELODY REVELANT
volatile boolean B = false; // becomes true when there is a heart pulse
volatile boolean QS = false; // becomes true when pulse rate is determined. every 20 pulses
int pulsePin = 0; // pulse sensor purple wire connected to analog pin 0
int speakerOut = 10;
void setup()
{
pinMode(speakerOut, OUTPUT);
pinMode(10,OUTPUT); // pin 13 will blink to your heartbeat! //MELODY REVELANT
Serial.begin(115200); // we agree to talk fast!
// this next bit will wind up in the library. it initializes Timer1 to throw an interrupt every 1mS.
TCCR1A = 0x00; // DISABLE OUTPUTS AND BREAK PWM ON DIGITAL PINS 9 & 10
TCCR1B = 0x11; // GO INTO 'PHASE AND FREQUENCY CORRECT' MODE, NO PRESCALER
TCCR1C = 0x00; // DON'T FORCE COMPARE
TIMSK1 = 0x01; // ENABLE OVERFLOW INTERRUPT (TOIE1)
ICR1 = 8000; // TRIGGER TIMER INTERRUPT EVERY 1mS
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
}
//========
#define c 3830 // 261 Hz
#define d 3400 // 294 Hz
#define e 3038 // 329 Hz
#define f 2864 // 349 Hz
#define g 2550 // 392 Hz
#define a 2272 // 440 Hz
#define b 2028 // 493 Hz
#define C 1912 // 523 Hz
#define R 0
int melody[] = { C, b, g, C, b, e, R, C, c, g, a, C };
int beats[] = { 16, 16, 16, 8, 8, 16, 32, 16, 16, 16, 8, 8 };
int MAX_COUNT = sizeof(melody) / 2; // Melody length, for looping.
// Set overall tempo
long tempo = 10000;
// Set length of pause between notes
int pause = 1000;
// Loop variable to increase Rest length
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES
// Initialize core variables
int tone_ = 0;
int beat = 0;
long duration = 0;
// PLAY TONE ==============================================
// Pulse the speaker to play a tone for a particular duration
void playTone() {
long elapsed_time = 0;
if (tone_ > 0) { // if this isn't a Rest beat, while the tone has
// played less long than 'duration', pulse speaker HIGH and LOW
while (elapsed_time < duration) {
digitalWrite(speakerOut,HIGH);
delayMicroseconds(tone_ / 2);
// DOWN
digitalWrite(speakerOut, LOW);
delayMicroseconds(tone_ / 2);
// Keep track of how long we pulsed
elapsed_time += (tone_);
}
}
else { // Rest beat; loop times delay
for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
delayMicroseconds(duration);
}
}
}
//========
void loop()
{
//=====================================
/*
// Set up a counter to pull from melody[] and beats[]
for (int i=0; i<MAX_COUNT; i++) {
tone_ = melody[i];
beat = beats[i];
duration = beat * tempo; // Set up timing
playTone();
// A pause between notes...
delayMicroseconds(pause);
}*/
//=====================================
// Serial.print("S"); // S tells processing that the following string is sensor data
//Serial.println(Signal); // Signal holds the latest raw Pulse Sensor signal
if (B == true)
{
//==============================
/*
// B is true when arduino finds the heart beat
for (int i=0; i<MAX_COUNT; i++) {
tone_ = melody[i];
beat = beats[i];
duration = beat * tempo; // Set up timing
playTone();
// A pause between notes...
delayMicroseconds(pause);
}
*/
//============================
//Serial.print("B"); // 'B' tells Processing the following string is HRV data (time between beats in mS)
//Serial.println(HRV); // HRV holds the time between this pulse and the last pulse in mS
B = false; // reseting the QS for next time
}
if (QS == true)
{
//==============================
/*
// B is true when arduino finds the heart beat
for (int i=0; i<MAX_COUNT; i++) {
tone_ = melody[i];
beat = beats[i];
duration = beat * tempo; // Set up timing
playTone();
// A pause between notes...
delayMicroseconds(pause);
}
*/
//============================
// QS is true when arduino derives the heart rate by averaging HRV over 20 beats
Serial.print("Q"); // 'QS' tells Processing that the following string is heart rate data
Serial.println(BPM); // BPM holds the heart rate in beats per minute
QS = false; // reset the B for next time
}
Fade -= 15;
Fade = constrain(Fade,0,255);
analogWrite(11,Fade);
delay(20); // take a break
//BPM IS VARIABLE
//Serial.println(analogValue); // print it
// get a sensor reading:
// map the results from the sensor reading's range
// to the desired pitch range:
if(BPM > 100 && BPM < 200)
{
//==== for (int i=0; i<BPM; i++) {
tone_ = melody[BPM/17];
beat = beats[BPM/17];
//12 in the arrays---<?>
duration = BPM; // Set up timing
playTone();
//===}
digitalWrite(10, HIGH);
Serial.print("1st playtone working in BPM if");
//tone(8, frequency, 100);
}
else
{
tone_ = melody[BPM/17];
beat = beats[BPM/17];
//Serial.print("1st playtone working in BPM else");
//12 in the arrays---<?>
duration = BPM; // Set up timing
playTone();
digitalWrite(10,HIGH);
Serial.print("second playtone working in BPM else");
}
// change the pitch, play for 10 ms:
//tone(8, frequency, 100);
}
// THIS IS THE TIMER 1 INTERRUPT SERVICE ROUTINE. IT WILL BE PUT INTO THE LIBRARY
ISR(TIMER1_OVF_vect){ // triggered every time Timer 1 overflows
// Timer 1 makes sure that we take a reading every milisecond
Signal = analogRead(pulsePin);
// First normailize the waveform around 0
readings += Signal; // take a running total
sampleCounter++; // we do this every milisecond. this timer is used as a clock
if ((sampleCounter %300) == 0)
{ // adjust as needed
offset = readings / 300; // average the running total
readings = 0; // reset running total
}
NSignal = Signal - offset; // normalizing here
// IF IT'S THE FIRST TIME THROUGH THE SKETCH, SEED THE FILTER WITH CURRENT DATA
if (first = true)
{
for (int i=0; i<4; i++)
{
Lxv[i] = Lyv[i] = NSignal <<10; // seed the lowpass filter
Hxv[i] = Hyv[i] = NSignal <<10; // seed the highpass filter
}
first = false; // only seed once please
}
// THIS IS THE BANDPAS FILTER. GENERATED AT www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html
// BUTTERWORTH LOWPASS ORDER = 3; SAMPLERATE = 1mS; CORNER = 5Hz
Lxv[0] = Lxv[1]; Lxv[1] = Lxv[2]; Lxv[2] = Lxv[3];
Lxv[3] = NSignal<<10; // insert the normalized data into the lowpass filter
Lyv[0] = Lyv[1]; Lyv[1] = Lyv[2]; Lyv[2] = Lyv[3];
Lyv[3] = (Lxv[0] + Lxv[3]) + 3 * (Lxv[1] + Lxv[2])
+ (3846 * Lyv[0]) + (-11781 * Lyv[1]) + (12031 * Lyv[2]);
// Butterworth; Highpass; Order = 3; Sample Rate = 1mS; Corner = .8Hz
Hxv[0] = Hxv[1]; Hxv[1] = Hxv[2]; Hxv[2] = Hxv[3];
Hxv[3] = Lyv[3] / 4116; // insert lowpass result into highpass filter
Hyv[0] = Hyv[1]; Hyv[1] = Hyv[2]; Hyv[2] = Hyv[3];
Hyv[3] = (Hxv[3]-Hxv[0]) + 3 * (Hxv[1] - Hxv[2])
+ (8110 * Hyv[0]) + (-12206 * Hyv[1]) + (12031 * Hyv[2]);
FSignal = Hyv[3] >> Scale; // result of highpass shift-scaled
//PLAY AROUND WITH THE SHIFT VALUE TO SCALE THE OUTPUT ~12 <> ~20 = High <> Low Amplification.
if (FSignal >= Peak && Pulse == false){ // heart beat causes ADC readings to surge down in value.
Peak = FSignal; // finding the moment when the downward pulse starts
peakTime = sampleCounter; // recodrd the time to derive HRV.
}
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
if ((sampleCounter %20) == 0){// only look for the beat every 20mS. This clears out alot of high frequency noise.
if (FSignal < 0 && Pulse == false){ // signal surges down in value every time there is a pulse
Pulse = true; // Pulse will stay true as long as pulse signal < 0
//digitalWrite(10,HIGH);
// Serial.print("tone is coming from sample counter if"); // pin 13 will stay high as long as pulse signal < 0
Fade = 255; // set the fade value to highest for fading LED on pin 11 (optional)
HRV = peakTime - lastPeakTime; // measure time between beats
lastPeakTime = peakTime; // keep track of time for next pulse
B = true; // set the Quantified Self flag when HRV gets updated. NOT cleared inside this ISR
rate += HRV; // add to the running total of HRV used to determine heart rate
beatCounter++; // beatCounter times when to calculate bpm by averaging the beat time values
if (beatCounter == 10){ // derive heart rate every 10 beats. adjust as needed
rate /= beatCounter; // averaging time between beats
BPM = 60000/rate; // how many beats can fit into a minute?
beatCounter = 0; // reset counter
rate = 0; // reset running total
QS = true; // set Beat flag when BPM gets updated. NOT cleared inside this ISR
}
}
if (FSignal > 0 && Pulse == true)
{ // when the values are going up, it's the time between beats
// Serial.print("tone is coming from FSignal if");
// digitalWrite(10,LOW); // so turn off the pin 13 LED
Pulse = false; // reset these variables so we can do it again!
Peak = 0; //
}
}
}// end isr