How to count rectangular signals and calculate signal/minute?

edited December 2016 in Arduino

Hello! I have little expierence with programming and I had followed some instructions to make a light sensitive counter that would peak every time there's a sufficient change in ambient or artificial lighting. I took an example build of a heartrate sensor and adapted it for this purpose.

I was wondering If there's a way for processing to count signal position and display a calculated minute average of signals. For example 25 signals in 10 seconds multiplied by 6 to get signals per minute.

Example signal would be

LightSensor

The processing code i'm using is taken from an example about a heartrate sensor.

How would I go about making the code detect a signal like this and count it once it goes over a certain treshold?

import processing.serial.*;

Serial myPort; // The serial port
int xPos = 1; // horizontal position of the graph
float oldHeartrateHeight = 0; // for storing the previous reading

void setup () {
  // set the window size:
  size(600, 400);
  frameRate(25);

  // List available serial ports.
  println(Serial.list());

  // Setup which serial port to use.
  // This line might change for different computers.
  myPort = new Serial(this, Serial.list()[1], 9600);

  // set inital background:
  background(0);
}

void draw () {
}

void serialEvent (Serial myPort) {
  // read the string from the serial port.
  String inString = myPort.readStringUntil('\n');

  if (inString != null) {
    // trim off any whitespace:
    inString = trim(inString);
    // convert to an int
    println(inString);
    int currentHeartrate = int(inString);

    // draw the Heartrate BPM Graph.
    float heartrateHeight = map(currentHeartrate, 0, 1023, 0, height);
    stroke(0,255,0);
    line(xPos - 1, height - oldHeartrateHeight, xPos, height - heartrateHeight);
    oldHeartrateHeight = heartrateHeight;
    // at the edge of the screen, go back to the beginning:
    if (xPos >= width) {
      xPos = 0;
      background(0);
    } else {
      // increment the horizontal position:
      xPos++;
    }
  }
}
Tagged:

Answers

  • Firstly, you would need a variable (Integer) count and a variable (Boolean) above_threshold. Then, each time serialEvent() is called, check in the end if it has gone above a certain threshold. If so, check if above_threshold is false. If so, then increment count and set above_threshold to true. Now, you also must check if the value in question has gone below the threshold. If so, then set above_threshold to false.
    Try it and post your code here.

  • edited December 2016

    Do not issue draw commands to sketch's canvas outside the Animation Thread! [-X

  • From what I gather i assume it's pretty easy to do as Lord says, however I really have no programming background. The before mentioned code is taken just in order to display the peaks.

    From what I could, i wrote only this, how can i to set the code to do an action after it compares if the value is above or below threshold?

    int count;
    int above_threshold;
    int threshold = 600;
    
    //in serial event//
    
    if (above_threshold < threshold) {\
    if (above_threshold > threshold) { //Start counting? how?
    

    This is not really anything I need for the project, it would be just a little added bonus for myself to actually automate it somewhat. Am I headed in the right direction? @Lord

  • Here is an idea how to count pulses. This program is an example that generates its own data. The data has a range of 0 to 60 and pulses are counted on the signal's leading edge right when the signal crosses the threshold. The threshold was set to be at 45, or at 75% of the signal.

    This algorithm has been not been tested rigorously. So use it at your own risk in a way.

    There are three steps you need to do to setup the PulseCounter engine:
    1. Set the min,max and threshold using setYpar() function
    2. Set the time resolution using setTimeRes() in Hz., the frequency of your incoming data.
    3. Add data points to the PulseCounter object by using PulseConter::append() function.

    Data is displayed in draw across the width of the sketch. When a sketch width is filled, the PulseCounter obj is reset (but not the calculated pulse rate measured in the window).

    To get the pulse rate, there are two functions that you can call in draw():
    1. getContinuousRate() is continuously updated during the sketch and reset every time the data displayed fills the width of the screen. it is an estimate and it becomes more accurate as more data is captured.
    2. getFinalWinRate() is updated only after a full sketch'es width is full. Before the PulseCounter object is reset to display the next incoming data, the pulse rate in the whole window is calculated. Therefore this value is only updated after a reset is triggered by the system. This value will be more consistent to the true rate of the incoming signal.

    Notice a pulse is counted only when it crosses the threshold. it has to become lower than threshold in order to be able to count a new pulse.

    I hope this works for you.

    Kf

    int sval=color(20, 250, 20);
    PulseCounter pc;
    
    int myDataMin=0;
    int myDataMax=60;
    int myDataThreshold=45;  
    
    void setup() {
      size(600, 400);
    
      fill(250, 20, 20);
      background(92);
      rectMode(CENTER);
      textAlign(CENTER, CENTER);
    
    
      pc = new PulseCounter();         //Starts pulse counter manager
      pc.setTimeRes(30);               //Time resolution of input data in Hz
      frameRate(pc.getTimeRes());      //Match to set time resolution... not mandatory, but required for this working example
    
    
      //Set min, max value range of input data. Set threshold value PulseCounter to trigger counts
      //input data is assume to be positive (for this first version anyways) so min range must be 
      //greater than max range. Threshold is a value above the min range. When the signal crosses 
      //this value, it generates a count. While the signal is above threshold, counter is disabled.
      //Counter is re-enabled only after the signal crosses back the threshold value.
      pc.setYpar(myDataMin,myDataMax, myDataThreshold);
    }
    
    void draw() {
    
      pc.append(frameCount%myDataMax);  //Fake generated Generated data. 
      pc.showThreshold();                            //Draws the threshold line across the display
      pc.updateDisplay();                              //Draws latest point
      pc.checkIfNewCount();                         //Counter analysis
    
    
    
      fill(20, 20, 200);
      rect(width*0.50, height*0.075, width*0.20, height*0.10);
      fill(0);
      text("Pulses "+nfc(pc.getContinuousRate(), 4)+"\nWin Rate= " + nfc(pc.getFinalWinRate(), 4), width/2, height*0.075);
    }
    
    
    void mouseReleased() {
      pc.resetCounter();
    }
    
    
    
    //  ############################################################################
    
    class PulseCounter {
    
      IntList pts;
    
      float time;
      float timeRes;
    
      float min, max, thresh;
      int pulseCtr=0;
      private boolean highState;
    
      private float cRate;         //Continuous rate
      private float winFinalRate;
    
    
      PulseCounter() {
        min=max=thresh=0;
        pts=new IntList();
        cRate=1.0;  //Init
        winFinalRate=-1;
        time=-1;
        timeRes=-1;
        highState=false;
      }
    
      int size() {
        return pts.size();
      }
    
      void append(int val) {
        pts.append(val);
      }
    
      void increaseCounter() { 
        pulseCtr++;
      }
    
      int getCounts() {
        return pulseCtr;
      }
    
      void setTime(float atime) {
        time=atime;
      }
    
      float getTime() {
        return time;
      }
    
      float getTimeRes() {
        return timeRes;
      }
    
      void setTimeRes(float atime) {
        time=-1;
        timeRes=atime;
      }
    
      float getContinuousRate() {
        cRate=-1;
        if (time>0)
          cRate=pulseCtr/(float)time;
        else if (timeRes>0)
          cRate=pulseCtr/((float)size())*timeRes;
        return cRate;
      }
    
      float getFinalWinRate()
      {
        return(winFinalRate);
      }
    
      void resetCounter() {
        pts.clear();
        pulseCtr=0;
        background(92);
      }
    
      void setThresh(float aval) {
        winFinalRate=-1;
        resetCounter();
        thresh=aval;
      }
    
      float getThresh() {
        return(thresh);
      }
    
      void setYpar(float _min, float _max, float _thresh) {
        min=_min;
        max=_max;
    
        setThresh(_thresh);
    
        if (max<min) {
          println("ERROR: Invalid min-max values. Min must be larger than max value. Min and max set to zero.");
          min=max=0;
        }
      }
    
      float  getMinValue() {
        return(min);
      }
    
      float  getMaxValue() {
        return(max);
      }
    
      float  dataYinHeightSpace(int yVal) {
        return(map(yVal, min, max, 0, height));
      }
    
      void showThreshold() {
        //Draw threshold line
        stroke(255);
        int ythr=round(dataYinHeightSpace((int)getThresh()));
        line(0, ythr, width, ythr);
      }
    
      void displayAllPoints() {
        //background(92);
        int y0=0, y1=0;
        for (int i=1; i<pc.size(); i++) {  //STARTS @1
    
          //ellipse(i, map(pts.get(i),-200,200,0,height), 5, 5);
          stroke(0);
          y0=round(dataYinHeightSpace(pts.get(i-1)));
          y1=round(dataYinHeightSpace(pts.get(i)));
    
          line(i-1, y0, i, y1);
        }
      }
    
      void updateDisplay() {
        int ictr=pts.size()-1;
    
        //Plotting current and last point.
        //This next avoids null point error
        if (ictr<=0)
          return;
    
        stroke(0);
        int y0=round(dataYinHeightSpace(pts.get(ictr-1)));
        int y1=round(dataYinHeightSpace(pts.get(ictr)));
    
        line(ictr-1, y0, ictr, y1);
      }
    
    
      void checkIfNewCount() {
    
        int y1=pts.get(size()-1);//round(dataYinHeightSpace(pts.get(size()-1)));   //Retrive last point entered
    
        //Only register count by detecting rising edge
        if (highState==false) {
          if (y1>pc.getThresh()) {
            stroke(255);
            line(pts.size()-1, 0, pts.size()-1, height);
            pulseCtr++;
            highState=true;
          }
        } else {
          if (y1<pc.getThresh()) {
            highState=false;
          }
        }
    
        CheckIfScreenLimitReached();
      }
    
      void CheckIfScreenLimitReached() {
        //Reset
        if (size()>width) {
          winFinalRate=getContinuousRate();
          resetCounter();
        }
      }
    }
    
  • Wow, really good answer from @kfrajer. Mine is just a simplistic, basic solution.

Sign In or Register to comment.