Hi everyone, my first post. I have created a PPG circuit that detects variation in blood flow and outputs a square wave with each pulse ( square wave due to high gain and saturation of OP amp outputs).
I have been working on writing a code that detects the number of pulses and finding the frequency over 6 seconds, then i'll multiply by 10 for BPM. Here's my code so far:
int pin = 13;
int timecount = 0; // time between pulses
int sumtime = 0; // summation of all time between pulses
int currtime = 0; // current pulse time - found in interrupt
int pulsefreq = 0; // frequency of pulses
volatile int pulsecounter = 0; //counter incremented with every pulse detection
volatile int state = LOW; // initialise LED status as LOW
volatile int pulsetime = 0; // initialise time when a pulse occurs
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, pulse, RISING);
Serial.begin(9600);
}
void loop()
{
digitalWrite(pin, state);
//--Calculating time of pulses--//
currtime = pulsetime; //previous / current time of pulse
timecount = currtime-timecount; //finding time difference between pulses
sumtime = timecount+sumtime; //summing all the time differences
//else pcount = pcount;
//----Finding frequency of pulses---//
if (pulsecounter=6)
pulsefreq = 6/sumtime;
delay(1000);
Serial.println(pulsefreq);
}
void pulse()
{
state = !state;
pulsetime = millis();//reading time at which pulse occurs
pulsecounter=pulsecounter + 1;//increment pulseee counter
}
You should avoid using interrupts as far as possible, and they aren't necessary here. You can just read the state of the input and look for changes in the state.
Just counting the number of complete beats over a few seconds will give you a very poor resolution because you would be rounding down the first and last incomplete beats - you could do a lot better by recording the cumulative duration of all the beats you detected during those six seconds and then multiply the count by (60 seconds / cumulative duration).
There's absolutely nothing wrong with using interrupts. They are there to be used.
Some of the problems with your code:
millis() returns an unsigned long integer but you are treating it as just a 16-bit integer. This will not work once the Arduino has been running for more than 32767 milliseconds when the number will overflow the capacity of a signed 16-bit integer.
the code in loop() doesn't need to do anything until a pulse has been detected. Set a flag in the interrupt routine. In loop() don't do anything until the flag is set.
if(pulsecounter=6) will set pulsecounter to 6 and always execute the conditional statement. You should use if(pulsecounter == 6)
pulsefreq = 6/sumtime; This will always produce zero because it is an integer division. It is also the wrong calculation.
el_supremo:
There's absolutely nothing wrong with using interrupts. They are there to be used.
Nothing wrong with using interrupts where necessary, but using interrupts introduces extra design issues which are best avoided unless there is a compelling reason to use them. For example, you haven't mentioned (yet) that access to multi-byte variables is not atomic so the accesses to those volatile variables in the main context isn't safe. I don't think there are any race conditions in this code but it took me a while to come to that conclusion and I doubt the OP looked for that sort of problem.
There is no compelling reason to use interrupts here and the sketch would be much simpler without them.
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks status of digital pin 2
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
unsigned long int timecount = 0;//cumulative time of total pulses
unsigned long int freq=0;
void setup()
{
pinMode(LED, OUTPUT);
pinMode(din,INPUT);
Serial.begin(9600);
}
void loop()
{
check = digitalRead(din);
if (check = 1023)
digitalWrite(LED, HIGH);
timecount = millis() + timecount;
pulsecount = pulsecount +1;
if (pulsecount = 6)
freq = 6/ timecount;
digitalWrite(LED,LOW);
pulsecount = 0;
Serial.println(freq);
delay(1000);
}
it gives me a read of a very large number then goes to 0 and holds till reset
willing2learn:
is something like this what you mean?:
I'm afraid there are so many typos in that code that there's no chance of it doing anything useful. You really need to focus on the difference between = assignment and == comparison operators, and using { and } after if/else statements to show which statements are controlled by the if/else. Also note that digitalRead() returns either HIGH or LOW, not an integer that could ever equal 1023.
The StateChangeDetection example sketch shows how to read the input and detect when it has changed. When it has changed you need to execute a bit of code which does the following:
If this is the first pulse detected, record the value of millis() as the time of the first pulse and set the number of beats to zero. Otherwise record the value of millis() as the time of the last pulse and increment the number of beats.
Use the technique demonstrated in Blink Without Delay to determine when the elapsed time exceeds six seconds. When that happens, subtract the time of the first pulse from the time of the last pulse to get the duration of the whole beats detected. You already have a count of the number of whole beats. Now it's simple arithmetic to calculate the number of beats per minute.
i made some changes to the code - adding the flag and correcting the declarations:
int pin = 13;
int long timecount = 0; // time between pulses
int long sumtime = 0; // summation of all time between pulses
unsigned long int currtime = 0; // current pulse time - found in interrupt
int long pulsefreq = 0; // frequency of pulses
volatile int pulsecounter = 0; //counter incremented with every pulse detection
volatile int state = LOW; // initialise LED status as LOW
volatile unsigned long int pulsetime = 0; // initialise time when a pulse occurs
int flag; //check if pulse detected
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, pulse, RISING);
Serial.begin(9600);
}
void loop()
{
if (flag == 1) // go through loop if flag is set
{
digitalWrite(pin, state);
//--Calculating time of pulses--//
currtime = pulsetime; //previous / current time of pulse
timecount = currtime-timecount; //finding time difference between pulses
sumtime = timecount+sumtime; //summing all the time differences
//else pcount = pcount;
//----Finding frequency of pulses---//
if (pulsecounter==6)
{
pulsefreq = (360/sumtime); // 60 *6/sumtime
flag = 0; // set flag low till next peak detected
pulsecounter =0; // reset the counter
}
}
if (flag == 0); // bypass the loop if flag is still low
{
flag=0;
}
delay(1000);
Serial.println(pulsefreq);
}
void pulse()
{
state = !state;
pulsetime = millis();//reading time at which pulse occurs
pulsecounter=pulsecounter + 1;//increment pulse counter
flag=1;
}
I have been unable to figure out why the pulsefreq calculation was wrong?
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 6000; //setting 6 seconds as max read time
void setup()
{
pinMode(LED, OUTPUT);
pinMode(din,INPUT);
Serial.begin(9600);
}
void loop()
{
check = digitalRead(din);
if (check != prevcheck ) // comparing current state to previous state
{
if (check == HIGH)
{
digitalWrite(LED, HIGH);
if (pulsecount ==1); // if this is the first count
{
firstpulse = millis(); // recording time of first pulse
//pulsecount = 0; //assigning 0 to counter
}
{
pulsecount = pulsecount +1; // increment pulse counter when detected
//timecount = millis() + firstpulse + timecount; //sumation of all pulse periods
latestpulse = millis(); // reading latest pulse time
}
if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
{
BPM = pulsecount * 10;
Serial.println(BPM);
}
}
delay(1000);
}
}
Though the LED blinks, there is nothing displayed on the serial print screen
willing2learn:
Hi PeterH, i have made changes to the code:
On line 31 you have a spurious semicolon at the end of the line which renders the whole line ineffective.I think you should also be comparing against zero to find whether it's the initial pulse. You also seem to have spurious parenthesis on lines 38 and 42 which could be deleted.
Wouldn't it simplify things with the interrupt - then the processor would exactly when i want the loops to run (at rising edge)?. At the moment it only detects any change - therefore keeping the LED on.
The rest of the code doesn't run as expected even with the changes:
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 6000; //setting 6 seconds as max read time
void setup()
{
pinMode(LED, OUTPUT);
pinMode(din,INPUT);
Serial.begin(9600);
}
void loop()
{
check = digitalRead(din);
if (check != prevcheck ) // comparing current state to previous state
{
if (check == HIGH)
{
digitalWrite(LED, HIGH);
if (pulsecount == 0 ) // if this is the first count
{
firstpulse = millis(); // recording time of first pulse
//pulsecount = 0; //assigning 0 to counter
}
pulsecount = pulsecount +1; // increment pulse counter when detected
//timecount = millis() + firstpulse + timecount; //sumation of all pulse periods
latestpulse = millis(); // reading latest pulse time
}
}
if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
{
BPM = pulsecount * 10;
Serial.println(BPM);
pulsecount = 0; // counter restarted
delay(1000);
}
}
The printed value does change when the time interval is reached, but only displays zeroes or 10s at each second after
Wouldn't it simplify things with the interrupt - then the processor would exactly when i want the loops to run (at rising edge)?. At the moment it only detects any change - therefore keeping the LED on.
The rest of the code doesn't run as expected even with the changes:
Interrupts, as PeterH mentioned, are not necessary here. Furthermore, they bring new hazards to trip you up and can be hard to debug. I'd suggest, looking at the problems you're having, that you should leave them alone until you've got a bit more experience.
The reason that the LED stays on, sadly, is simply that you never do anything to turn it off.
Another omission that's causing you trouble is that you're trying to use prevcheck to detect a change in state on the digital pin. prevcheck is never touched, so it retains its original zero value throughout the sketch, which isn't helping.
I have made changes to the code. The LED flashes in time with the heartbeat. But now nothing is printed on the screen monitor:
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 6000; //setting 6 seconds as max read time
void setup()
{
pinMode(LED, OUTPUT);
pinMode(din,INPUT);
Serial.begin(9600);
}
void loop()
{
check = digitalRead(din);
// if (check != prevcheck ) // comparing current state to previous state
//{
if (check == LOW)
{
digitalWrite(LED, LOW);
// check = prevcheck;
}
if (check == HIGH)
{
digitalWrite(LED, HIGH);
// check = prevcheck;
if (pulsecount == 0 ) // if this is the first count
{
firstpulse = millis(); // recording time of first pulse
pulsecount = pulsecount +1; //increment the counter
}
if (pulsecount >=1 )
{
pulsecount = pulsecount + 1; // increment pulse counter when detected
//timecount = millis() + firstpulse + timecount; //sumation of all pulse periods
latestpulse = millis(); // recording latest pulse time
}
}
//}
if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
{
BPM = pulsecount;
Serial.println(BPM); //display the number of counts over 6 seconds
//pulsecount = 0; // counter restarted
firstpulse = 0; // restart for time of new first-pulse read
latestpulse = 0; // restart for new time readings
//delay(1000);
}
}
You need your prevcheck logic so that you only increment pulsecount when you detect a change. As you have it, I suspect that pulsecount overflows within the six seconds and allows a reset of first pulse so that the six second interval is never achieved.
Now the LED flashes correctly - with the heartbeat, but there is nothing printed. I've also tried going back, but still nothing is printed. Here's the code now:
int LED = 13; //LED to blink when pulsing
int din = 2; // digital pin 2 to read 0V or 5V
int check; // checks current status of digital pin 2
int prevcheck;// previous check status
int pulsecount = 0; //number of pulses (highs) seen on the digital pin
int BPM = 0; //beats perminute = counter value * 10
unsigned long firstpulse =0; //time of first pulse
unsigned long timecount = 0;//cumulative time of total pulses
unsigned long latestpulse=0;
unsigned long interval = 6000; //setting 6 seconds as max read time
void setup()
{
pinMode(LED, OUTPUT);
pinMode(din,INPUT);
Serial.begin(9600);
}
void loop()
{
check = digitalRead(din);
//if (check != prevcheck ) // comparing current state to previous state
{
if (check == LOW)
{
digitalWrite(LED, LOW);
//prevcheck == LOW;
}
if (check == HIGH)
{
digitalWrite(LED, HIGH);
//prevcheck == HIGH; //storing current state of input pin
check == LOW; //reset the check
if (pulsecount == 0 ) // if this is the first count
{
firstpulse = millis(); // recording time of first pulse
pulsecount = pulsecount +1; //increment the counter
}
if (pulsecount >=1 )
{
pulsecount = pulsecount + 1; // increment pulse counter when detected
//timecount = millis() + firstpulse + timecount; //sumation of all pulse periods
latestpulse = millis(); // recording latest pulse time
}
}
}
if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
{
BPM = pulsecount;
Serial.println(BPM); //display the number of counts over 6 seconds
pulsecount = 0; // counter restarted
firstpulse = 0; // restart for time of new first-pulse read
latestpulse = 0; // restart for new time readings
//delay(100);
}
prevcheck = check;
}
void loop()
{
check = digitalRead(din);
if(check != prevcheck)
{
if (check == LOW)
{
digitalWrite(LED, LOW);
}
if (check == HIGH)
{
digitalWrite(LED, HIGH);
if (pulsecount == 0 ) // if this is the first count
{
firstpulse = millis(); // recording time of first pulse
pulsecount = pulsecount +1; //increment the counter
}
if (pulsecount >=1 )
{
pulsecount = pulsecount + 1; // increment pulse counter when detected
latestpulse = millis(); // recording latest pulse time
}
}
}
if (latestpulse - firstpulse > interval) //checking if 6 seconds has elapsed
{
BPM = pulsecount;
Serial.println(BPM); //display the number of counts over 6 seconds
pulsecount = 0; // counter restarted
firstpulse = 0; // restart for time of new first-pulse read
latestpulse = 0; // restart for new time readings
}
prevcheck = check;
}