Printing a number interfering with plot

Hi guys. I’ve been trying to use a pulse sensor (Pulse Sensor Amped : ID 1093 : $25.00 : Adafruit Industries, Unique & fun DIY electronics and kits) to plot heartbeat and measure beats per minute. In order to calculate beats per minute, I’ve been using code from the following project: http://www.xtronical.com/basics/heart-beat-sensor-ecg-display/

Problem:
The problem I’ve been having is that printing the BPM piece of code makes my single plot turn into five plots.

Goal:
Accurate BPM measurements with single plot of heartbeat.

Attempt:
Here’s my code.

#include <Adafruit_GFX.h>
#include <UTFTGLUE.h>
UTFTGLUE myGLCD(0x9488,A2,A1,A3,A4,A0);

#if !defined(SmallFont)
extern uint8_t SmallFont[];
#endif

  int buf[478];
  int x = 0;
  int LastTime = 0;
  bool BPMTiming = false;
  bool BeatComplete = false;
  int BPM = 0;
  #define UpperThreshold 560
  #define LowerThreshold 500

void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(5));
  pinMode(A0, OUTPUT); 
  digitalWrite(A0, HIGH);
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.clrScr();

}

void loop()
{ 
  for (int i=1; i<(477*30); i++) 
  {
    delay(20);
    x++;
    if (x==478)
      x=1;
    if (i>478)
    {
      if ((x==239)||(buf[x-1]==159))
        myGLCD.setColor(0,0,255);
      else
        myGLCD.setColor(0,0,0);
        myGLCD.drawPixel(x,buf[x-1]);
    }
        myGLCD.setColor(0,255,255);

    int value=analogRead(5);
    int y=(value/3);      //Responsible for Y-axis.
    myGLCD.drawPixel(x, y);
    buf[x-1]=y;
  
  // Calculate when a beat has been completed
  if(value>UpperThreshold)
  {
    if(BeatComplete)
    {
      BPM=millis()-LastTime;
      BPM=int(60/(float(BPM)/1000));
      BPMTiming=false;
      BeatComplete=false;
    }
    if(BPMTiming==false)
    {
      LastTime=millis();
      BPMTiming=true;
    }
  }
  if((value<LowerThreshold)&(BPMTiming))
    BeatComplete=true;

  // Display BPM
  myGLCD.setColor(255,255,255); 
  myGLCD.printNumI(BPM, 40, 34); <---- This line is the problem, I think.
  
  }
}

When “myGLCD.printNumI(BPM, 40, 34);” is commented out, the wave looks fine, as shown in the attached image. When it is not commented out, the wave looks like the second image, with multiple waves overlapping.

I’ve tried moving it to different positions in the loop to see if that changes anything. I can’t figure out why it’s having that effect when all it is doing is displaying a calculated integer.

Could anyone give me a hint as to why it may be doing this, or suggest a different way of displaying the required value? I’ve been through the UTFT manual and the only command that allows me to print an integer is “myGLCD.printNumI”. Perhaps someone knows another way around this, or why the interaction is happening. Thank you.

      else

myGLCD.setColor(0,0,0);
        myGLCD.drawPixel(x,buf[x-1]);

This would be so much more readable if you format the indenting properly. Use the auto-format to show you how it’s done. Multi-line if() statements without braces are asking for trouble. Always use braces {} when the statement has more than one line.

It’s hard to tell from that motion-affected photo but it looks like maybe you are measuring a frequency much higher than you think. What is the source of this input we are seeing? Small changes to the timing (making it slower by adding more work) will significantly change the display of out-of-range frequencies.

Change the timing at the top to use a specific time regardless of the time that each loop actually takes. Something like:

void loop()
{
  for (int i = 1; i < (477 * 30); i++)
  {
    //delay(20);
    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();
    x++;

Also, why are you printing BPM for every single pixel? Only print it when the BPM is actually updated.

The reading is coming from the sensor I linked in my original post. It’s a pulse sensor from Adafruit.

Adding braces to the if statements made them not work anymore, for example:

    if ((value < LowerThreshold) & (BPMTiming))
      BeatComplete = true;

    // Display BPM when calculated at the bottom of the loop
    myGLCD.setColor(255, 255, 255);
    // myGLCD.printNumI(BPM, 40, 34);

changed to

    if ((value < LowerThreshold) & (BPMTiming))
{
      BeatComplete = true;

    // Display BPM when calculated at the bottom of the loop
    myGLCD.setColor(255, 255, 255);
    // myGLCD.printNumI(BPM, 40, 34);
}

no longer works as it did before. I am not a programmer by any means so I’m not sure why.

This section of code:

  for (int i = 1; i < (477 * 30); i++)
  {
    //delay(20);
    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();
    x++;

which you suggested replacing with this section of code:

void loop()
{
  for (int i = 1; i < (477 * 30); i++)
  {
    //delay(20);
    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();
    x++;

no longer works the way it did, with pixels overwriting previous ones when the x-axis resets to the left side of the screen after going past the right side. I got that section of code straight from one of the TFT examples. Were you suggesting it was related to the problem I’m encountering?

I wasn’t trying to print BPM for every pixel. I’ve tried placing “myGLCD.printNumI(BPM, 40, 34)” elsewhere in the code but it doesn’t work anywhere else for me.

You got the braces wrong on the if(). When there's no braces, only the next statement ending in ";" is part of the if(). You enclosed another line in the braces.

Did you end up with two copies of "x++"? Show your complete program.

Here is the full code with auto-formatting so hopefully it’s easier to read.

#include <Adafruit_GFX.h>
#include <UTFTGLUE.h>
UTFTGLUE myGLCD(0x9488, A2, A1, A3, A4, A0);

#if !defined(SmallFont)
extern uint8_t SmallFont[];
#endif

int buf[478];
int x = 0;

int LastTime = 0;
bool BPMTiming = false;
bool BeatComplete = false;
int BPM = 0;

#define UpperThreshold 550
#define LowerThreshold 500

void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(5)); 
  pinMode(A0, OUTPUT);    
  digitalWrite(A0, HIGH);
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.clrScr();

}


void loop()
{
  // Draw the beat
  for (int i = 1; i < (477 * 30); i++)
  {
    delay(20);
    x++;
    if (x == 478)
      x = 1;
    if (i > 478)      // This if statement redraws the screen if the x counter passes the border
    {
      if ((x == 239) || (buf[x - 1] == 159))
        myGLCD.setColor(0, 0, 255);
      else
        myGLCD.setColor(0, 0, 0);
      myGLCD.drawPixel(x, buf[x - 1]);
    }
    myGLCD.setColor(0, 255, 255);

    int value = analogRead(5);
    int y = (value / 3);              //Responsible for Y-axis.
    myGLCD.drawPixel(x, y);
    buf[x - 1] = y;


    // Calculate when a beat has been completed
    if (value > UpperThreshold)
    {
      if (BeatComplete)
      {
        BPM = millis() - LastTime;
        BPM = int(60 / (float(BPM) / 1000));
        BPMTiming = false;
        BeatComplete = false;
      }
      if (BPMTiming == false)
      {
        LastTime = millis();
        BPMTiming = true;
      }
    }
    
    if ((value < LowerThreshold) & (BPMTiming))
      BeatComplete = true;
    

    // Display BPM
    myGLCD.setColor(255, 255, 255);
    myGLCD.printNumI(BPM, 40, 34);

  }
}

When I added braces to the following statement that you highlighted, it ceased to work:

     if ((x == 239) || (buf[x - 1] == 159))
        myGLCD.setColor(0, 0, 255);
      else
      myGLCD.setColor(0, 0, 0);
      myGLCD.drawPixel(x, buf[x - 1]);

With brackets:

     if ((x == 239) || (buf[x - 1] == 159))
     {
        myGLCD.setColor(0, 0, 255);
      else
      myGLCD.setColor(0, 0, 0);
      myGLCD.drawPixel(x, buf[x - 1]);
     }

I went on to try to incorporate your suggestion, but I was struggling to work out where to put it, or what to replace with it. I ended up trying the following setup, but was unable to glean information from it.

void loop()
{
 for (int i = 1; i < (477 * 30); i++)
  {
    //delay(20);
    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) 
    {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();
    x++;
    }
}

Very likely the incorrect thing to do, but I simply placed your code in the center of my loop between the block which draws the wave and the block which calculates BPM, just to see what would happen. The result was a single line being drawn as desired, though much faster than it was, and a BPM being shown (although it isn’t correct). I think it’s progress, at least! By doing this, I did end up with a second copy of “x++”.

void loop()
{
  // Draw the beat
  for (int i = 1; i < (477 * 30); i++)
  {
    delay(20);
    x++;
    if (x == 478)
      x = 1;
    if (i > 478)      // This if statement redraws the screen if the x counter passes the border
    {
      if ((x == 239) || (buf[x - 1] == 159))
        myGLCD.setColor(0, 0, 255);
      else
        myGLCD.setColor(0, 0, 0);
      myGLCD.drawPixel(x, buf[x - 1]);
    }
    myGLCD.setColor(0, 255, 255);

    int value = analogRead(5);
    int y = (value / 3);              //Responsible for Y-axis.
    myGLCD.drawPixel(x, y);
    buf[x - 1] = y;


    //-------Your code start-------//

    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) 
    {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();
    x++;                   // Second copy of x counter

    //-------Your code end-------//


    // Calculate when a beat has been completed
    if (value > UpperThreshold)
    {
      if (BeatComplete)
      {
        BPM = millis() - LastTime;
        BPM = int(60 / (float(BPM) / 1000));
        BPMTiming = false;
        BeatComplete = false;
      }
      if (BPMTiming == false)
      {
        LastTime = millis();
        BPMTiming = true;
      }
    }
    
    if ((value < LowerThreshold) & (BPMTiming))
      BeatComplete = true;
    

    // Display BPM
    myGLCD.setColor(255, 255, 255);
    myGLCD.printNumI(BPM, 40, 34);

  }
}

Always use braces {} when the statement has more than one line.

That's better now.

I’m prepared to omit braces when it’s a single-line if, for example:

if(a==b) c();

And even though it’s legal to write the else on the same line too, I usually do use braces and multiple lines when there’s an else.

So, back to the original problem.From reply #4, you took my code which included the void loop() and put it in the middle of the code? Why would you do that? I thought I included enough context to show where my changes fitted. Basically I’m replacing the delay(20); If you rip out that single line and replace with the code below, you should have no problem…

    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) 
    {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();

So, fixing braces…
If you don’t use braces after an if() then ONLY the ONE single line below the if() is considered to be part of the if(). So this…

     if ((x == 239) || (buf[x - 1] == 159))
        myGLCD.setColor(0, 0, 255);
      else
      myGLCD.setColor(0, 0, 0);
      myGLCD.drawPixel(x, buf[x - 1]);

is equivalent to…

     if ((x == 239) || (buf[x - 1] == 159)) {
        myGLCD.setColor(0, 0, 255);
      } else {
      myGLCD.setColor(0, 0, 0);
      }
    myGLCD.drawPixel(x, buf[x - 1]);

The compiler doesn’t see spaces. It can’t see that the last line was indented to look like it’s part of the else part.

I placed it in the center as I wasn’t sure where to put it, so I tried placing it in various parts of the program to see how it affected things. After you explaining it just now, I believe I’ve placed it correctly. Thanks very much for that. The below block of code shows where I’ve placed what you suggested (replaced the “delay(20);”), and I’ve also fixed the braces of that if statement as directed.

void loop()
{
  // Draw the beat
  for (int i = 1; i < (477 * 30); i++)
  {


   //-------Your code start-------//

    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) 
    {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();

    //-------Your code end-------//


    x++;
    if (x == 478)
      x = 1;
    if (i > 478)      // This if statement redraws the screen if the x counter passes the border
    {
      if ((x == 239) || (buf[x - 1] == 159)) {   <---Fixed braces (Hopefully)
        myGLCD.setColor(0, 0, 255);
      } else {
        myGLCD.setColor(0, 0, 0);
        myGLCD.drawPixel(x, buf[x - 1]);
      }
    }
    myGLCD.setColor(0, 255, 255);

    int value = analogRead(5);
    int y = (value / 3);              //Responsible for Y-axis.
    myGLCD.drawPixel(x, y);
    buf[x - 1] = y;


    // Calculate when a beat has been completed
    if (value > UpperThreshold)
    {
      if (BeatComplete)
      {
        BPM = millis() - LastTime;
        BPM = int(60 / (float(BPM) / 1000));
        BPMTiming = false;
        BeatComplete = false;
      }
      if (BPMTiming == false)
      {
        LastTime = millis();
        BPMTiming = true;
      }
    }
    
    if ((value < LowerThreshold) & (BPMTiming))
      BeatComplete = true;
    

    // Display BPM
    myGLCD.setColor(255, 255, 255);
    myGLCD.printNumI(BPM, 40, 34);

  }
}

I can confirm after uploading the below code, that the beat is being detected correctly. It’s not yet being drawn correctly, as there are two lines of pixels drawing the beat, and the beat is very small, but this is certainly progress. BPM is not being displayed correctly, but I suspect that’s just because the boundaries haven’t been set correctly by me yet. When the magnitude of the beat is nailed down, I expect I can start working on the boundaries to detect a beat then.

I can now see the effect of using a specific time regardless of the time that each loop actually takes over using a delay of 20 milliseconds. I’ll remember that for the future. Thanks very much, Morgan.

About controlling the magnitude of the beat as displayed on my LCD; is it appropriate to simply multiply the value read by the sensor by another number? For example, currently the lines controlling the sensor data are as follows:

    int value = analogRead(5);
    int y = (value / 3);  //Responsible for Y-axis.

The sensor value is divided by 3 because otherwise the data is either off the screen, or it’s a flat line. In this tutorial, the same value is divided by 16 and it looks fine on his small LCD.

So the next issues I’ll be addressing are the multiple lines drawing the wave instead of one line, the magnitude, and then calculating the BPM. I’ll spend a few hours on those myself now that the beats are being read correctly, but if I do run into problems I can’t overcome from intensive Googling or experimentation, I’ll post in here again. Hints are always welcome! Thanks very much for your help thus far.

Having experimented with the code for a while now, I was wondering if there was any way to boost the amplitude of the signal whilst keeping the scaling the same. Basically a scaling feature rather than a multiplier. The reason I ask is because the magnitude of the beats is so low that they can hardly be seen on the LCD and can only just about be seen in the Plotter because of its autoscaling. If I ought to create an separate thread for this, I will do so.

So far, I’ve been through each of the examples in the software. They don’t seem to offer anything like what I seek in an obvious fashion. Google has been of limited help too. I’ll keep trying, but in the mean time, any advice would be greatly appreciated.

My pulse waveform certainly looks nothing like the one in this tutorial.

I have attached an image comparing the reading in the tutorial to my reading. The sensor being used is the same.

So you want the range 200-400 to show up on your LCD which is 360 pixels high? Is zero the top-left of your LCD?

Even if you have different answers to the above questions, the map function will save you:

  int y = map(value, 200, 400, 360, 0);

Much appreciated, Morgan. Thank you for that info.

The first attached image shows the readings from the sensor using your code in place of a delay:

    static unsigned long lastTime = 0;
    const unsigned long INTERVAL = 20; //milliseconds, calculated as 1 divided by sample frequency
    while(millis()-lastTime<INTERVAL) 
    {
      //wait; do nothing until the sample interval expires
    }
    lastTime = millis();

The second image shows the reading in the serial plotter if I use the following code, with a delay:

void setup() {
  Serial.begin(9600);
}
void loop() {
  Serial.println(analogRead(5));
  delay (20);
}

The difference between the two (aside from the amplitude) is that the first (your code) detects beats which correlate perfectly with what I feel. The second may display whatever is being detected perfectly, but the readings you see don’t actually correlate with my heartbeat well at all. This is to be expected with the delay.

Do you know of any way to achieve the fidelity of the second image but using a similar method to your code? I’ve been trying to add modifications myself but without success. I will continue to experiment in the mean time, but I figured I’d ask here too.

Building on my previous post, I’ve discovered that I can remove your code and the delay. Neither are necessary if I change “if (BeatComplete)” to “if (BeatComplete == true)” in

void loop() {
  
int value = analogRead(A5);

   if (value > UpperThreshold)
    {
      if (BeatComplete == true)
      {
        BPM = millis() - LastTime;
        BPM = int(60 / (float(BPM) / 1000));
        BPMTiming = false;
        BeatComplete = false;
      }
      if (BPMTiming == false)
      {
        LastTime = millis();
        BPMTiming = true;
      }
    }

    if ((value < LowerThreshold) & (BPMTiming))
    {
      BeatComplete = true;
     }

    myGLCD.setColor(255, 255, 255);
    myGLCD.printNumI(BPM, 40, 34);
    Serial.println(value);
}

What this tells me is that the amplitude issue is somehow caused by the above block of code. Now to figure out why it’s causing the readings to go behave as they do in the images I attached to my previous post.

I’ll also add that the PulsePlayground library doesn’t read the sensor correctly either. I’d imagine it should.

StringBean1:
Building on my previous post, I've discovered that I can remove your code and the delay. Neither are necessary if I change "if (BeatComplete)" to "if (BeatComplete == true)"

Those two statements are literally identical. Something else must have changed in between the two tests.

Did you connect the ground on the sensor?