Delay in Serial plot using Processing

edited October 2017 in Arduino

Hi all,

We are facing timing issue with serial plot in processing. Below are the details.

Description

We are using Processing to plot the incoming serial data. Incoming data is of 'Float' type & there is a value arriving at the serial port once in 4mS.

Expected Behavior

Ideally processing should plot the data immediately without any delay. In this case, data plotting will get completed as soon as the data arrival stops at the serial port.

Current Behavior

Processing is taking time to receive & plot each value from serial port. This delay to read serial data value & plot is around 16mS. Due to this delay, Processing plot will not be real time when compared to incoming serial data.

Steps to Reproduce

Send data at higher rate from to Serial port of PC (At a rate of 250Hz) Verify that the plotting takes higher time to plot compared to incoming data rate Below is the code we are using for the plot. I was unable to attached the code in this post, thus adding the code in the discussion itself.

import processing.serial.*;

String xLabel = "X-axis";

String yLabel = "Y-axis";

String Heading = "Test Plot";


Serial myPort;        // The serial port

int xPos = 1;         // horizontal position of the graph

float inByte = 0;

float inByte1=0,inByte2=0;

int input=0;

PFont font;

int text_offset=20;

int margin_offset=50;



void setup () 

{

// set the window size: 100 points taken for Margins

  size(1300,650);


// List all the available serial ports - For reference

  println(Serial.list());


// Select first port

  myPort = new Serial(this, Serial.list()[0], 115200);


// Don't generate a serialEvent() unless you get a newline character:

  myPort.bufferUntil('\n');


// set inital background:

  background(255);

}

void draw () 

{

  //Check if the new data is available  

  if (myPort.available() > 0)

  {

    String inString = myPort.readStringUntil('\n');

    if (inString != null) 

    {

      // convert to an int and map to the screen height:

      inByte = float(inString);

      // Copy the old data to inByte2 & copy new data to inByte1

      inByte2=inByte1;

      inByte1=inByte;

      // Draw a line between inByte1 & inByte2

      line(xPos+margin_offset-1, height-inByte2-margin_offset, xPos+margin_offset, height - inByte1-margin_offset);  

      // Reset if the edge of the screen is reached

      if (xPos >= width) 

      {

        //Reset xPos 

        xPos = 0;

        //Reset background 

        background(255);

      } 

      else

      {

          // increment the horizontal position:

          xPos++;

      }

    }

  }

}

Environment

Processing version: 3.3.6 Operating System and OS version: Windows 8 Is there any way to decrease this delay so that data can be plotted real time. Looking forward to valuable suggestions.

Thanks, Suresha

Answers

  • Hi,

    Thanks for your response.

    I tried using SerialEvent , noLoop & redraw functions. Even though the logic works, the plot is not fast enough to receive all incoming serial data in real time.

    Below is the modified code -

    import processing.serial.*;
    String xLabel = "X-axis";
    String yLabel = "Y-axis";
    String Heading = "Test Plot";
    Serial myPort;        // The serial port
    int xPos = 1;         // horizontal position of the graph
    float inByte = 0;
    float inByte1=0,inByte2=0;
    int input=0;
    PFont font;
    int text_offset=20; 
    int margin_offset=50;
    
    void setup () 
    {
    
    // set the window size: 100 points taken for Margins
      size(1300,650);
    
    // List all the available serial ports - For reference
      println(Serial.list());
    
    // Select first port
      myPort = new Serial(this, Serial.list()[0], 115200);
    
    // Don't generate a serialEvent() unless you get a newline character:
      myPort.bufferUntil('\n');
    
    // set inital background:
      background(255);
    
      noLoop();  
    }
    
    void draw () 
    {
    
          // Copy the old data to inByte2 & copy new data to inByte1
          inByte2=inByte1;
          inByte1=inByte;
    
          // Draw a line between inByte1 & inByte2
          line(xPos+margin_offset-1, height-inByte2-margin_offset, xPos+margin_offset, height - inByte1-margin_offset);  
    
         // Reset if the edge of the screen is reached
          if (xPos >= width) 
         {
    
            //Reset xPos 
            xPos = 0;
    
            //Reset background 
            background(255);
          } 
    
          else
          {
              // increment the horizontal position:
              xPos++;
          }
    }
    
    
    void serialEvent (Serial myPort) 
    {
       // get the ASCII string:
       String inString = myPort.readStringUntil('\n');
    
       if (inString != null) 
       {
           inByte = float(inString);
           redraw();
       }
     }
    

    If I add frameRate(200) in the setup function, then the plot happens almost 1.75 times faster. But even this is not enough for the serial data to be plotted in real time. Increasing the frameRate beyond 200 doesn't result in any improvements.

    Is there any other way to increase the plotting speed ? Looking forward to valuable suggestions.

  • Answer ✓

    My understanding is that if your have a input rate 250Hz, processing should be able to handle it or, in better words, you should manage it properly. For this, you need to use the serialEvent() function as shown by @GoToLoop's post.

    As you know, Processing is usually running at 30 fps. When you use the serialEvent() function, you are not constrained to the draw's upper limit for frame handling. What you need to do, in case the serial event is not as fast, is that all the input data from the serial stream will be accumulated in the serial's input buffer. When you get a handle of the serialEvent(), you should access and stored all the data from the input buffer. A demonstration is done in:

    https://processing.org/reference/libraries/serial/Serial_readString_.html

    Notice in the demonstration they use a while() inside the draw() function. My suggestion is to use the serialEvent() but I am guess either one should work.

    To plot the data though, you are constrained to the screen's refreshing rate of 30fps. For an initial input rate of 250Hz, every time draw() runs, it will add not one point but about 8 points since 250Hz/30Hz=8 approx. For this concept to wok in your challenge, I will do it this way:

    1. Create a dynamic Array with global scope (or a FloatList object)
    2. Populate the array with all the data present in the input buffer. Do this in serial event.
    3. In draw, make sure you draw all the items in your array object.

    One final recommendation is to use a ring data structure instead of a standard list or array object. I hope this helps,

    Kf

  • Hi ,

    Thanks a lot for your suggestions.

    I modified the code as below & I am able to plot at 250Hz now.

    import processing.serial.*;
    Serial myPort;        // The serial port
    int xPos = 1;         // horizontal position of the graph
    float inByte = 0;
    float inByte1=0,inByte2=0;
    int input1=0;
    PFont font;
    int text_offset=20;
    int margin_offset=50;
    int i=0;
    int count = 0;
    String inString;
    float [] input;
    
    void setup () 
    {
      // set the window size: 100 points taken for Margins
      size(1300,650);
    
      // List all the available serial ports - For reference
      println(Serial.list());
    
      // Select first port
      myPort = new Serial(this, Serial.list()[0], 115200);
    
      // Don't generate a serialEvent() unless you get a newline character:
      myPort.bufferUntil('\n');
    
      // set inital background:
      background(255);
    }
    
    void draw () 
    {
        while (myPort.available() > 0) 
        {
            inString = myPort.readString();
            input1 = 1;
        }
    
        if (input1 == 1)
        {
            input = float(splitTokens(inString));  
            count = input.length;
            input1 = 0;
    
        for (i=0;i<count; i++)
            {
    
            // Copy the old data to inByte2 & copy new data to inByte1
                inByte2=inByte1;
                inByte1=input[i];
    
                // Draw a line between inByte1 & inByte2
                line(xPos+margin_offset-1, height-inByte2-margin_offset, xPos+margin_offset, height - inByte1-margin_offset);  
    
                // Reset if the edge of the screen is reached
                if (xPos >= width) 
                {
                    //Reset xPos 
                    xPos = 0;
    
            //Reset background 
                    background(255);
                } 
                else
                {
                    // increment the horizontal position:
                    xPos++;
                }
    
            }
    
        }
    
    }
    

    Really appreciate your help !!

    Thanks,

    Suresha

Sign In or Register to comment.