Delay occurs in real time plotting using threads

I’m using 7 serial ports with 7 sensors required to be plotted in rela time without any delays. I have used separate thread functions to plot the data. However, after 5 minutes of plotting I have observed there is a delay of real time plots. How can I get rid of this delay?

import processing.serial.*;
import grafica.*;
import controlP5.*;
import g4p_controls.*;
import signal.library.*;

PrintWriter output;   // save into a file
PrintWriter output2;   // save into a file
PrintWriter output3;   // save into a file
PrintWriter output4;   // save into a file
PrintWriter output5;   // save into a file

PrintWriter output6;   // save into a file
PrintWriter output7;   // save into a file
PrintWriter output8;   // save into a file
PrintWriter output9;   // save into a file
PrintWriter output10;   // save into a file

Serial Port1;          // create a serial port object
String val;            // Serial port reading

Serial Port2;          // create a serial port object
String val2;            // Serial port reading

Serial Port3;          // create a serial port object
String val3;            // Serial port reading

Serial Port4;          // create a serial port object
String val4;            // Serial port reading

Serial Port5;          // create a serial port object
String val5;            // Serial port reading

Serial Port6;          // create a serial port object
String val6;            // Serial port reading

Serial Port7;          // create a serial port object
String val7;            // Serial port reading

int nPoints = 60000;  

public String timestamp = str(hour()) + str(minute()) + str(second());
public float i=0, j=0, k=0, m=0, ic=0, jc=0, kc=0, mc=0;
public float ii=0,jj=0,kk=0,mm=0,iic=0,jjc=0,kkc=0,mmc=0;
public GPlot plot1, plot2, plot3, plot4;


public  GPointsArray points = new GPointsArray(nPoints);    // create output saving array
public  GPointsArray points1 = new GPointsArray(nPoints);

public  GPointsArray points2 = new GPointsArray(nPoints);
public  GPointsArray points3 = new GPointsArray(nPoints);

public  GPointsArray points4 = new GPointsArray(nPoints);
public  GPointsArray points5 = new GPointsArray(nPoints);

public  GPointsArray points6 = new GPointsArray(nPoints);


public  GPointsArray points7 = new GPointsArray(nPoints);
public  GPointsArray points8 = new GPointsArray(nPoints);

public  GPointsArray points9 = new GPointsArray(nPoints);
public  GPointsArray points10 = new GPointsArray(nPoints);

public  GPointsArray points11 = new GPointsArray(nPoints);
public  GPointsArray points12 = new GPointsArray(nPoints);

public  GPointsArray points13 = new GPointsArray(nPoints);

ControlP5 cp5;    //create controller object

PFont font;        //create font object

SignalFilter myFilter;      // Create the filter
SignalFilter myFilter2;      // Create the filter

// Main OneEuroFilter parameters
float freq =120;
float minCutoff = 3.0; // decrease this to get rid of slow speed jitter
float beta      = 0.007;  // increase this to get rid of high speed lag
float filteredSignal,filteredSignal2;
String signal,signal2;


int numReadings = 5;

float [] readings= new float[numReadings];                 // the readings from the analog input
int readIndex = 0;              // the index of the current reading
float total = 0;                  // the running total
float average = 0;                // the average

float [] readings2= new float[numReadings];                 // the readings from the analog input
int readIndex2 = 0;              // the index of the current reading
float total2 = 0;                  // the running total
float average2 = 0;                // the average


// Start of void setup

void setup(){
  
  size(1500, 1000);   // canvas size
  smooth();
  noStroke();
  background(50);
  frameRate(100);
  
  output = createWriter( "ReadingIMP.csv" );  // data saving file name
  output2 = createWriter( "ReadingSPO2.csv" );  // data saving file name
  output3=createWriter("Indexlist.csv");
  output4 = createWriter( "ReadingTemp.csv" );  // data saving file name
  output5 = createWriter( "ReadingPAT.csv" );  // data saving file name
  
  output6 = createWriter( "ReadingIMPC.csv" );  // data saving file name
  output7 = createWriter( "ReadingSPO2C.csv" );  // data saving file name
  output8=createWriter("IndexlistC.csv");
  output9 = createWriter( "ReadingTempC.csv" );  // data saving file name
  output10 = createWriter( "ReadingPATC.csv" );  // data saving file name
  
  String ports[]=Serial.list();         // finding available serial ports
  for (String port : ports) {
    println(port);
  }
  
  
  Port1=new Serial(this,ports[0], 250000);     // Open the serial port
  Port1.bufferUntil('\n');
  
  Port2=new Serial(this, ports[4], 250000);     // Open the serial port
  Port2.bufferUntil('\n');
  
  Port3=new Serial(this, ports[6], 250000);     // Open the serial port
  Port3.bufferUntil('\n');
  
  Port4=new Serial(this, ports[1],250000);     // Open the serial port
  Port4.bufferUntil('\n');
  
  Port5=new Serial(this, ports[3], 250000);     // Open the serial port
  Port5.bufferUntil('\n');
  
  Port6=new Serial(this, ports[5], 250000);     // Open the serial port
  Port6.bufferUntil('\n');
  
  Port7=new Serial(this, ports[2],250000);     // Open the serial port
  Port7.bufferUntil('\n');
  
  
  cp5= new ControlP5(this);
  font =createFont("calibri light", 18);


  cp5.addButton ("Start")    //label the button
    .setPosition(1300, 50)    //position of the button
    .setSize(100, 80)       //width and height
    .setFont(font)
    .setColorBackground(color(50, 50, 255))
    .setColorForeground(color(50, 100, 200))
    // .setColorValue(color(255,255,0))
    .setColorActive(color(50, 150, 150));

  cp5.addButton ("Baseline")    //label the button
    .setPosition(1300, 150)    //position of the button
    .setSize(100, 80)        //width and height
    .setFont(font)
    .setColorBackground(color(255, 150, 50))
    .setColorForeground(color(200, 150, 50))
    .setColorActive(color(150, 100, 150));

  cp5.addButton ("Occlusion")    //label the button
    .setPosition(1300, 250)    //position of the button
    .setSize(100, 80)        //width and height
    .setFont(font)
    .setColorBackground(color(255, 0, 0))
    .setColorForeground(color(200, 50, 50))
    .setColorActive(color(150, 100, 150));

  cp5.addButton ("Release")    //label the button
    .setPosition(1300, 350)    //position of the button
    .setSize(100, 80)        //width and height
    .setFont(font)
    .setColorBackground(color(0, 255, 50))
    .setColorForeground(color(50, 200, 50))
    .setColorActive(color(50, 150, 150));

  cp5.addButton ("Stop")    //label the button
    .setPosition(1300, 450)    //position of the button
    .setSize(100, 80)        //width and height
    .setFont(font)
    .setColorBackground(color(100, 50, 255))
    .setColorForeground(color(50, 100, 200))
    .setColorActive(color(50, 150, 150));
  
  // Initialize the filter
  myFilter = new SignalFilter(this,1);
  myFilter.setFrequency(freq);
  myFilter.setMinCutoff(minCutoff);
  myFilter.setBeta(beta);
  
   // Initialize the filter
  myFilter2 = new SignalFilter(this,1);
  myFilter2.setFrequency(freq);
  myFilter2.setMinCutoff(minCutoff);
  myFilter2.setBeta(beta);
  
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  
for (int thisReading2 = 0; thisReading2 < numReadings; thisReading2++) {
    readings2[thisReading2] = 0;
  }
  
  
  
  
  plot1 = new GPlot(this);                   // ploting the graph for Impedance 5 kHz
  plot1.setPos(50, 50);
  plot1.setDim(400, 250);
  plot1.setAxesOffset(0);
  plot1.getTitle().setText("Impedance @ 5 kHz and 50 kHz");
  plot1.getXAxis().getAxisLabel().setText("Number of Samples");
  plot1.getYAxis().getAxisLabel().setText("Ohms");
  plot1.setYLim(300, 3000);
  plot1.setFixedYLim(true);
  plot1.setLineColor(color(0, 0, 255));
  
  
  plot2 = new GPlot(this);                   // ploting the graph for SPO2
  plot2.setPos(700, 50);
  plot2.setDim(400, 250);
  plot2.setAxesOffset(0);
  plot2.getTitle().setText("IR and Red Raw signal");
  plot2.getXAxis().getAxisLabel().setText("Number of Samples");
  plot2.getYAxis().getAxisLabel().setText("Absorption Value");
  // plot2.setYLim(60000, 120000);
  // plot2.setFixedYLim(true);
  plot2.setLineColor(color(0, 0, 255));
  
  
  plot3 = new GPlot(this);                   // ploting the graph for Body Temperature
  plot3.setPos(50, 450);
  plot3.setDim(400, 250);
  plot3.setAxesOffset(0);
  plot3.getTitle().setText("Body Temperature and Ambient Temperature");
  plot3.getXAxis().getAxisLabel().setText("Number of Samples");
  plot3.getYAxis().getAxisLabel().setText("Celcius");
  plot3.setYLim(25, 37);
  plot3.setFixedYLim(true);
  plot3.setLineColor(color(255, 0, 0));
  
  
  plot4 = new GPlot(this);                   // ploting the graph for Arterial Pressure
  plot4.setPos(700, 450);
  plot4.setDim(400, 250);
  plot4.setAxesOffset(0);
  plot4.getTitle().setText("Arterial Pressure");
  plot4.getXAxis().getAxisLabel().setText("Number of Samples");
  plot4.getYAxis().getAxisLabel().setText("mmHg");
  plot4.setYLim(60, 90);
  plot4.setFixedYLim(true);
  plot4.setLineColor(color(255, 0, 255));

  
}



void draw() {
  
  
  
  plot1.setPoints(points);               // ploting the graph for Impedance 5 kHz
  plot1.addLayer("layer 4", points1);
  plot1.getLayer("layer 4").setLineColor(color(0, 200, 0));
  plot1.getLayer("layer 4").setLineWidth(3);
  plot1.addLayer("layer 5", points7);
  plot1.getLayer("layer 5").setLineColor(color(0, 50, 200));
  plot1.getLayer("layer 5").setLineWidth(3);
  plot1.addLayer("layer 6", points8);
  plot1.getLayer("layer 6").setLineColor(color(0, 200, 50));
  plot1.getLayer("layer 6").setLineWidth(3);
  plot1.beginDraw();
  plot1.drawBackground();
  plot1.drawXAxis();
  plot1.drawYAxis();
  plot1.drawTitle();
  plot1.drawGridLines(GPlot.BOTH);
  plot1.drawLines();
  plot1.setLineColor(color(0, 0, 255));
  plot1.setLineWidth(3);
  plot1.activatePanning();
  plot1.activateZooming(2, CENTER, CENTER); 
  plot1.endDraw();
  plot1.removeLayer("layer 4");
  plot1.removeLayer("layer 5");
  plot1.removeLayer("layer 6");
  
  
  plot2.setPoints(points2);               // ploting the graph for SPO2
  plot2.addLayer("layer 2", points3);
  plot2.getLayer("layer 2").setLineColor(color(100, 100, 200));
  plot2.getLayer("layer 2").setLineWidth(3);
  plot2.addLayer("layer 7", points9);
  plot2.getLayer("layer 7").setLineColor(color(200, 0, 100));
  plot2.getLayer("layer 7").setLineWidth(3);
  plot2.addLayer("layer 8", points10);
  plot2.getLayer("layer 8").setLineColor(color(100, 0, 200));
  plot2.getLayer("layer 8").setLineWidth(3);
  plot2.beginDraw();
  plot2.drawBackground();
  plot2.drawXAxis();
  plot2.drawYAxis();
  plot2.drawTitle();
  plot2.drawGridLines(GPlot.BOTH);
  plot2.drawLines();
  plot2.setLineColor(color(255, 0, 0));
  plot2.setLineWidth(3);
  plot2.activatePanning();
  plot2.activateZooming(2, CENTER, CENTER); 
  plot2.endDraw();
  plot2.removeLayer("layer 2");
  plot2.removeLayer("layer 7");
  plot2.removeLayer("layer 8");
  
  
  plot3.setPoints(points4);               // ploting the graph for Temperature
  plot3.addLayer("layer 3", points5);
  plot3.getLayer("layer 3").setLineColor(color(100, 100, 200));
  plot3.getLayer("layer 3").setLineWidth(3);
  plot3.beginDraw();
  plot3.drawBackground();
  plot3.drawXAxis();
  plot3.drawYAxis();
  plot3.drawTitle();
  plot3.drawGridLines(GPlot.BOTH);
  plot3.drawLines();
  plot3.setLineColor(color(255, 0, 0));
  plot3.setLineWidth(3);
  plot3.activatePanning();
  plot3.activateZooming(2, CENTER, CENTER); 
  plot3.endDraw();
  plot3.removeLayer("layer 3");
  
  plot4.setPoints(points6);                // ploting the graph for Arterial Pressure
  plot4.addLayer("layer 9", points13);
  plot4.getLayer("layer 9").setLineColor(color(150, 100, 150));
  plot4.getLayer("layer 9").setLineWidth(3);
  plot4.beginDraw();
  plot4.drawBackground();
  plot4.drawXAxis();
  plot4.drawYAxis();
  plot4.drawTitle();
  plot4.drawGridLines(GPlot.BOTH);
  plot4.drawLines();
  plot4.setLineColor(color(255, 0, 255));
  plot4.setLineWidth(3);
  plot4.activatePanning();
  plot4.activateZooming(2, CENTER, CENTER);  
  plot4.endDraw();
  plot4.removeLayer("layer 9");
  
  
 
  thread("Impedance");
  thread("ImpedanceC");
  thread("SPO2");
  thread("SPO2C");
  thread("Temperature");
  thread("PAT");
  thread("PATC");
  
  
  
  
}


void Impedance() {

  if (Port1.available()>0) {
    // delay(1);
    val=Port1.readStringUntil('\n'); 
    ii=ii+1;
    
  if (val!=null && ii<=5){
      //println("ok");
      //println(j);
      println(val);
   }

  if (val!=null && ii>5 ) {
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list=split(val, ',');
      
      i=i+1;
      points.add(i/(24*60), float(list[0]));
      points1.add(i/(24*60), float(list[9]));
      output.println(timestamp + ", "  + list[0]+ ", "  + list[1]+", "  + list[2]+", "  + list[3]+", "  + list[4]+", "  + list[5]+
        ", "  + list[6]+", "  + list[7]+", "  + list[8]+", "  + list[9]+", "  + list[10]+", "  + list[11]);
    }
  }
}


void ImpedanceC() {

  if (Port5.available()>0) {
    // delay(1);
    val5=Port5.readStringUntil('\n'); 
    iic=iic+1;
    
  if (val5!=null && iic<=5){
      //println("ok");
      //println(j);
      println(val5);
   }

  if (val5!=null && iic>5 ) {
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list=split(val5, ',');
      
      ic=ic+1;
      points7.add(ic/(24*60), float(list[0]));
      points8.add(ic/(24*60), float(list[9]));
      output6.println(timestamp + ", "  + list[0]+ ", "  + list[1]+", "  + list[2]+", "  + list[3]+", "  + list[4]+", "  + list[5]+
        ", "  + list[6]+", "  + list[7]+", "  + list[8]+", "  + list[9]+", "  + list[10]+", "  + list[11]);
    }
  }
}



void SPO2() {
  if (Port2.available()>0) {
    //delay(50);
    val2=Port2.readStringUntil('\n'); 
    jj=jj+1;
    
    if (val2!=null && jj<=1){
      //println("ok");
      //println(j);
      println(val2);
   }
   if (val2!=null && jj>=2) {
      val2 = trim(val2);
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list=split(val2, ',');

      j=j+1;
      points2.add(j/(25*60), float(list[0]));
      points3.add(j/(25*60), float(list[1]));
      output2.println(timestamp + ", "  + list[0]+ ", "  + list[1]);
    }
  }
}


void SPO2C() {
  if (Port6.available()>0) {
    //delay(50);
    val6=Port6.readStringUntil('\n'); 
    jjc=jjc+1;
    
    if (val6!=null && jjc<=1){
      //println("ok");
      //println(j);
      println(val6);
   }
   if (val6!=null && jjc>=2) {
      val6 = trim(val6);
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list=split(val6, ',');

      jc=jc+1;
      points9.add(jc/(25*60), float(list[0]));
      points10.add(jc/(25*60), float(list[1]));
      output7.println(timestamp + ", "  + list[0]+ ", "  + list[1]);
    }
  }
}

void Temperature() {

  if (Port3.available()>0) {
    //delay(50);
    val3=Port3.readStringUntil('\n'); 
    kk=kk+1;
    
    if (val3!=null && kk<=1){
      //println("ok");
      //println(j);
      println(val3);
   }
  
  
  if (val3!=null && kk>=2) {              // Temperature reading
   // val3=Port3.readStringUntil('\n'); 
    if (val3!=null) {
      val3 = trim(val3);
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list=split(val3, ',');
      k=k+1;
      points4.add(k/(10*60), float(list[0]));
      points5.add(k/(10*60), float(list[1]));
      output4.println(timestamp + ", "  + list[0]+ ", "  +list[1]);
    }
   }
  }
}


void PAT() {

  while(Port4.available()>0) {                  // PAT signal reading
    //delay(1);
    val4=Port4.readStringUntil('\n'); 

    if (val4!=null && val4.charAt(0)=='#') {
      val4 = trim(val4);
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list1=split(val4, '#');
      //String list1=val4;
      // println(timestamp);
      // println(list1[1]);
      m=m+1;
      //filteredSignal = myFilter.filterUnitFloat(float(list1[1]));
     
      float y=Movavg(float(list1[1]));
     
      //points6.add(m, filteredSignal);
      //signal=nf(filteredSignal,0,0);
      //points6.add(m, filteredSignal);
      //points6.add(m, float(list1[1]));
      points6.add(m/(92*60), y);
      output5.println(timestamp +", "  + list1[1]);
    }
  }
}


void PATC() {

  while(Port7.available()>0) {                  // PAT signal reading
    //delay(1);
    val7=Port7.readStringUntil('\n'); 

    if (val7!=null && val7.charAt(0)=='#') {
      val7 = trim(val7);
      String timestamp = str(hour()) + str(minute()) + str(second());
      String[] list1=split(val7, '#');
      //String list1=val4;
      // println(timestamp);
      // println(list1[1]);
      mc=mc+1;
     // filteredSignal2 = myFilter2.filterUnitFloat(float(list1[1]));
     
      float yc=Movavg2(float(list1[1]));
     
      //points6.add(m, filteredSignal);
      //signal=nf(filteredSignal,0,0);
      //points6.add(m, filteredSignal);
      //points6.add(m, float(list1[1]));
      points13.add(mc/(92*60), yc);
      output9.println(timestamp +", "  + list1[1]);
    }
  }
}

float Movavg(float input){
  
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = input;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;
  
  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  return average = total / numReadings;
  // send it to the computer as ASCII digits
  
}


float Movavg2(float input){
  
  // subtract the last reading:
  total2 = total2 - readings2[readIndex2];
  // read from the sensor:
  readings2[readIndex2] = input;
  // add the reading to the total:
  total2 = total2 + readings2[readIndex2];
  // advance to the next position in the array:
  readIndex2 = readIndex2 + 1;
  
  // if we're at the end of the array...
  if (readIndex2 >= numReadings) {
    // ...wrap around to the beginning:
    readIndex2 = 0;
  }

  // calculate the average:
  return average2 = total2 / numReadings;
  // send it to the computer as ASCII digits
  
}


void Start() {
  Port1.write('%');
  Port2.write('%');
  Port3.write('%');
  Port4.write('%');
  Port5.write('%');
  Port6.write('%');
  Port7.write('%');
}

void Baseline() {
  String timestamp = str(hour()) + str(minute()) + str(second());
  output3.println(timestamp + ","+ i+ ","+ j+ ","+ k+ ","+ m);
  output10.println(timestamp + ","+ ic+ ","+ jc+ ","+ kc+ ","+ mc);
  // output1.println(timestamp + ","+ i+ ","+ j+ ","+ k+ ","+ m);
}

void Occlusion() {
  String timestamp = str(hour()) + str(minute()) + str(second());
  output3.println(timestamp +","+ i+ ","+ j+ ","+ k+ ","+ m);
   output10.println(timestamp + ","+ ic+ ","+ jc+ ","+ kc+ ","+ mc);
  // output1.println(timestamp + ","+ i+ ","+ j+ ","+ k+ ","+ m);
}

void Release() {
  String timestamp = str(hour()) + str(minute()) + str(second());
  output3.println(timestamp +","+ i+ ","+ j+ ","+ k+ ","+ m);
   output10.println(timestamp + ","+ ic+ ","+ jc+ ","+ kc+ ","+ mc);
  //output1.println(timestamp + ","+ i+ ","+ j+ ","+ k+ ","+ m);
}


void Stop () {
  output.flush();  // Writes the remaining data to the file
  output.close();  // Finishes the file
  output2.flush();  // Writes the remaining data to the file
  output2.close();  // Finishes the file
  output3.flush();  // Writes the remaining data to the file
  output3.close();  // Finishes the file
  output4.flush();  // Writes the remaining data to the file
  output4.close();  // Finishes the file
  output5.flush();  // Writes the remaining data to the file
  output5.close();  // Finishes the file
  output6.flush();  // Writes the remaining data to the file
  output6.close();  // Finishes the file
  output7.flush();  // Writes the remaining data to the file
  output7.close();  // Finishes the file
  output8.flush();  // Writes the remaining data to the file
  output8.close();  // Finishes the file
  output9.flush();  // Writes the remaining data to the file
  output9.close();  // Finishes the file
  output10.flush();  // Writes the remaining data to the file
  output10.close();  // Finishes the file
  exit();  // Stops the program
}

Hi @KGS,

I am not sure if what I am saying is 100% correct, but why did you set the frame rate to 100? What is the sample output rate from the arduino for your data? Because you set the baud rate to 250000. So I am not sure if the threads are being executed at enough speed to keep with everything that the arduino might be sending.
Also there are a couple of things that I am questioning:
Do you really need to create 10 different output files? Writing to output files is time consuming.
Also your draw loop() is doing a lot for each plot. Do you need to be always setting the layers, line colors, etc in every draw loop?
I would also clear the serial buffer from time to time to make sure that you have enough space to receive your data

I can’t test your sketch but I hope it helps.
Best regards

2 Likes