Problem Plotting Using Grafica & serialEvent()

Hello, this is my first post. I’ve used Arduino for a couple of years now but I am new to processing. I’m trying to create a fixture that turns a stepper motor with an Arduino while simultaneously reads the torque output using a Mark10 force/torque indicator. I’ve successfully run the stepper motor in the cycle that I planned and I can read the Mark10 torque values using serialEvent. I’ve run into trouble trying to graph the values from the Mark10. I believe it has something to do with serialEvent because a stripped down version of this code allows me to plot the real-time value from the Mark10. Below is my problematic code, the stripped down code, and the correct graph created with the stripped down code, respectively. Any help is appreciated. Sorry if my code sucks; like I said I’m new to processing.

import processing.serial.*;
import grafica.*;
import cc.arduino.*;

Arduino arduino;
Table rawtable;
Table summarytable;
Serial mark10;  // Create object from Serial class

int dir = 5;
int en = 6;
int stepPin = 7;
float lockingTorque = -2.0;
float maximumlockingtorque = 0;
float initialsliptorque = 0;
float finalsliptorque = 0;

float[] initialSlipTorqueArray = new float[10000];
float[] finalSlipTorqueArray = new float[10000];
float[] lockTorqueArray = new float[10000];
int index = 0;

String passfail;
boolean permission1 = true;
boolean permission2 = false;
boolean permission3 = true;
boolean waitPermission = true;
float time1;
float time2;
float waitTimer;
float clearTime;

String val;      // Data received from the serial port
int reading = 0;
int cycle = 0;
float y;
int level = 1;
int step = 0; //rotational steps
int fullRotation = 400;
float faststepDelay = 3;
float slowstepDelay = 10*faststepDelay;
float lockstepDelay = 200*faststepDelay;
float pause = 1000.0;
int time;
int clearDelay = 1000;

float xval = 0;
float yval = 0;

GPlot plot1;

int nPoints = 100000;
GPointsArray points = new GPointsArray(nPoints);

void setup() {
  arduino = new Arduino(this, "COM15", 57600);
  mark10 = new Serial(this, "COM17", 115200);

  mark10.write("?C\r");
  mark10.bufferUntil('\n');

  rawtable = new Table();
  rawtable.addColumn("Reading");
  rawtable.addColumn("Torque");
  rawtable.addColumn("Raw Data");

  summarytable = new Table();
  summarytable.addColumn("Cycle");
  summarytable.addColumn("Status");
  summarytable.addColumn("Initial Slip Torque");
  summarytable.addColumn("Final Slip Torque");
  summarytable.addColumn("Locking Torque");

  arduino.pinMode(dir, Arduino.OUTPUT);
  arduino.pinMode(en, Arduino.OUTPUT);
  arduino.pinMode(stepPin, Arduino.OUTPUT);
  arduino.digitalWrite(dir, Arduino.HIGH);
  arduino.digitalWrite(en, Arduino.LOW);

  plot1 = new GPlot(this);
  size(700, 700);
  background (150);

  noLoop();
}

void draw() {
  for (int c = 0; c < 1; c++) { // Number of reliability cycles
    //if (cycle < 2) { // Number of reliability cycles
    cycle++;
    level = 1;
    index = 0;
    mark10.write("Z\r");
    clearTime = millis();
    while (millis() - clearTime <= clearDelay) {
      //waiting to Clear
    }
    while (level < 8) {
      xval++;
      yval = y;
      points.add(xval, yval);
      plotting();
      switch(level) {
      case 1: //slow half slip cycle
        stepFunc(slowstepDelay);
        if (step == 0.25*fullRotation) {
          wait(pause);
          if (waitPermission) {
            initialsliptorque = max(initialSlipTorqueArray);
            level++;
            step = 0;
            index = 0;
          }
        }
        break;
      case 2: //fast slip cycle
        stepFunc(faststepDelay);
        if (step == 0.5*fullRotation) {
          wait(pause);
          if (waitPermission) {
            level++;
            step = 0;
            arduino.digitalWrite(dir, Arduino.LOW);
          }
        }
        break;
      case 3: // lock cycle
        if ((y > lockingTorque) && waitPermission) {
          stepFunc(lockstepDelay);
        } else {
          wait(pause);
          if (waitPermission) {
            maximumlockingtorque = min(lockTorqueArray);
            level++;
            step = 0;
            arduino.digitalWrite(dir, Arduino.HIGH);
            index = 0;
          }
        }
        break;
      case 4: //back to zero
        if (y < 0 && waitPermission) {
          stepFunc(slowstepDelay);
        } else {
          if (waitPermission) {
            level++;
            step = 0;
          }
        }
        break;
      case 5: //slow half slip cycle
        stepFunc(slowstepDelay);
        if (step == 0.25*fullRotation) {
          wait(pause);
          if (waitPermission) {
            finalsliptorque = max(finalSlipTorqueArray);
            level++;
            step = 0;
            index = 0;
          }
        }
        break;
      case 6: //fast slip cycle
        stepFunc(faststepDelay);
        if (step == 0.5*fullRotation) {
          if (waitPermission) {
            level++;
            step = 0;
          }
        }
        break;
      case 7: //back to zero
        if (y < 0 && waitPermission) {
          stepFunc(slowstepDelay);
        } else {
          if (waitPermission) {
            level++;
            step = 0;
          }
        }
        break;
      }
    }
    tablesave();
    summarywrite();
  }
  mark10.stop();
  summarytablesave();
  exit();
}

void serialEvent(Serial mark10) { 
  val = mark10.readStringUntil('\n');
  // Did we actually read anything in?
  if (val != null)
  { // We did
    reading++;
    //print(reading);
    //print("\t");
    //println(val);
    y = float(val);
    if (level == 1) {
      initialSlipTorqueArray[index] = y;
      index++;
    } else if (level == 5) {
      finalSlipTorqueArray[index] = y;
      index++;
    } else if (level == 3) {
      lockTorqueArray[index] = y;
      index++;
    }
    //points.add(reading, y);
    //plotting();
    tablewrite();
    mark10.write("?C\r");
  }
}

void tablewrite()
{
  TableRow newRow = rawtable.addRow();
  newRow.setInt("Reading", reading);
  newRow.setFloat("Torque", y);
  newRow.setString("Raw Data", val);
}

void summarywrite()
{
  if (abs(initialsliptorque-finalsliptorque) < 0.5) {
    passfail = "Pass";
  } else {
    passfail = "Fail";
  }
  TableRow newRow = summarytable.addRow();
  newRow.setInt("Cycle", cycle);
  newRow.setString("Status", passfail);
  newRow.setFloat("Initial Slip Torque", initialsliptorque);
  newRow.setFloat("Final Slip Torque", finalsliptorque);
  newRow.setFloat("Locking Torque", maximumlockingtorque);
}

void tablesave()
{
  String path = "";
  saveTable(rawtable, path);
  reading = 0;
  points = new GPointsArray(nPoints);
  rawtable = new Table();
  rawtable.addColumn("Reading");
  rawtable.addColumn("Torque");
  rawtable.addColumn("Raw Data");
}

void summarytablesave() {
  String loc = "";
  String filetype = ".csv";
  String path = loc + "\\Reliability Summary" + filetype;
  saveTable(summarytable, path);
}

void wait(float waitTime) {
  if (waitPermission) {
    waitTimer = millis();
    waitPermission = false;
  }
  if (millis() - waitTimer >= waitTime) {
    waitPermission = true;
  }
}

void stepFunc(float stepTime) {
  if (waitPermission) {
    if (permission1) {
      arduino.digitalWrite(stepPin, Arduino.HIGH);
      permission1 = false;
      time1 = millis();
    } else if (permission2) {
      arduino.digitalWrite(stepPin, Arduino.LOW);
      permission2 = false;
      permission3 = false;
      time2 = millis();
    }
    if ((millis() - time1 >= faststepDelay) && (permission3 == true)) {
      permission2 = true;
    } 
    if ((millis() - time2 >= stepTime) && (permission3 == false)) {
      permission1 = true;
      permission2 = false;
      permission3 = true;
      step++;
    }
  }
}

void plotting()
{
  println("here");
  plot1 = new GPlot(this);
  plot1.setPos(0, 0);
  plot1.setDim(600, 600);
  plot1.getTitle().setText("Reliability Torque");
  plot1.getXAxis().getAxisLabel().setText("reading");
  plot1.getYAxis().getAxisLabel().setText("Torque [lbFin]");
  plot1.setPointSize(2);
  plot1.drawGridLines(GPlot.BOTH);
  plot1.setPoints(points);
  plot1.defaultDraw();
}
import processing.serial.*;  
import grafica.*;

Table table;

Serial myPort;  // Create object from Serial class
String val;      // Data received from the serial port
int reading = 0;
int cycle = 0;
float y;

GPlot plot1;

int nPoints = 100000;
GPointsArray points = new GPointsArray(nPoints);

void setup() {
  String portName = Serial.list()[0];
  myPort = new Serial(this, "COM17", 115200);

  table = new Table();
  table.addColumn("Reading");
  table.addColumn("Torque");

  size(700, 700);
  background (0);
}

void draw()
{
  myPort.write("?\r");
  while (myPort.available() > 0)
  {
    // Read a string in
    val = myPort.readStringUntil('\n');
    // Did we actually read anything in?
    if (val != null)
    { // We did
      reading = reading + 1;
      print(reading);
      print("\t");
      println(val);
      y = float(val);
      points.add(reading, y);
    }
  }
  plotting();
  tablewrite();
}

void plotting()
{
  plot1 = new GPlot(this);
  plot1.setPos(0, 0);
  plot1.setDim(600, 600);
  plot1.getTitle().setText("Reliability Torque");
  plot1.getXAxis().getAxisLabel().setText("reading");
  plot1.getYAxis().getAxisLabel().setText("Torque [lbFin]");
  plot1.setPointSize(2);
  plot1.drawGridLines(GPlot.BOTH);
  plot1.setPoints(points);
  plot1.defaultDraw();
}

void tablewrite()
{
  TableRow newRow = table.addRow();
  newRow.setInt("Reading", reading);
  newRow.setFloat("Torque", y);
}

1 Like

Are you using two serial ports?
This may very well be the issue if it is not configured and coded correctly.

It appears that you are using:

  • Arduino(Firmata) library to open one serial port
  • Processing serial library to open another serial port

Can you describe your hardware setup?
A list is easier to read than a wrap-around paragraph.

:)

1 Like

Do you get an actual error message in the Processing console pane (bottom black pane)? If so can you copy it here.

glv,

Thanks for responding. Yes, I’m using two serial ports and yes I’ve loaded standardFirmata onto my Arduino mega and processing serial is communicating with the Mark10. I’m using a USB splitter to communicate with both.

My laptop > USB splitter > Arduino Mega > Stepper motor driver > stepper motor
My laptop > USB splitter > Mark10

The Mark10 sends data whenever I request (mark10.write("?C\r");

quark,

I don’t get an error message I just a grey rectangle screen (whatever the window is called when you run any processing sketch except mine is the color called in “background”.

Update: instead of stopping the code prematurely, I let the code run through the whole cycle and at the very end the correct plot shows up. It seems to be working its just not updating in real-time on the screen.

1 Like

That is a positive!

I do not use Firmata or Grafica so will leave this in your goods hands.

I have written code to do this from scratch; it will take some effort on your part if you chose this route.

This is an example but code not posted:
Real time plot
It is:
Arduino > Serial > Processing
Processing > Network > Processing

:)

1 Like

glv,

Thanks for your help! I may use that route if I can’t figure this out.

If anyone is following I was able to fix my code. I THINK what was wrong is that I have a fundamental misunderstanding about how the draw() function works in Processing. It seems like draw() needs to actually start and stop in order to draw something to the screen. In my code above, draw() only runs once but code runs repeatedly within a while loop inside of draw(). Therefore, everything was working except the actual graph drawn to the screen. When my code finally stopped, the graph would suddenly appear. I rewrote my code so that the meat of the code was inside of it’s own function in order to allow draw() to start and stop as fast as possible. Unless the processing veterans object, the lesson of this topic is to always allow draw() to start and stop.

Hello,

Draw() will repeat 60 fps (frames per sec) unless something is blocking it or slowing it down.

In your code, the while() must complete before it exits and continues to the code in draw().
This may be why you have to wait; this is blocking code while serial data is being received.

Here is an example:

Arduino Code
int count;
void setup() 
  {
Serial.begin(115200);
  delay(1000);
  Serial.print('\n'); //same as Serial.println()
  Serial.print("incoming");
  Serial.print('\n'); //same as Serial.println()
  }
  
void loop() 
  {         
  Serial.print(count++); 
  Serial.print(',');
  for (int i = 0; i<1000; i++)
    {
    Serial.print(i);    //same as Serial.println()    
    Serial.print(',');
    delay(10);       
    }
  Serial.print('\n');   //same as Serial.println()
  delay(10);
  }

Arduino sends 1000 strings (from 0 to 1000) with a 10 ms pause between strings so this will take around 10+ sec to send.

Processing Code
import processing.serial.*;  

Serial myPort;

String val;

void setup()
  {
  size(700, 700);
  background (0);
  
  colorMode(HSB, 360);
  
  // List all the available serial ports
  printArray(Serial.list());
  
  String portName = Serial.list()[4];
  myPort = new Serial(this, portName, 115200);
  }

void draw()
  {
  //if (myPort.available() > 0)      //not blocking
  while (myPort.available() > 0)   //blocking
    {
    val = myPort.readStringUntil('\n');
    if (val != null)
      {
      println(val);
      }
    }
  plotting();
  }

void plotting()
  {
  background(random(360), 360, 360);
  //println("Plotting");
  }

Processing receives the data.
A while() loop (blocking) waits until all the data is received and then continues to plotting() which changes background every 10 sec.
An if() loop (not blocking) allows plotting() to execute and the background to be changed with each draw() cycle (60 fps).

You will have to wait until the first string is received in my example to see the blocking vs not blocking effect.

My preference is to use:
https://processing.org/reference/libraries/serial/serialEvent_.html

:)