Parsing Data in Processing thru Serial Port from Arduino + Pixy Cam


#1

Hello! New here and haven’t been able to really find anything super helpful for what I specifically need. I’m a super beginner…n00b, really. My professor is helping me with this but we haven’t quite figured it out yet and I thought I’d try here while I wait to meet with him again.

I have a Pixy2 Camera plugged up to an Arduino Uno, and I’m communicating data through the serial port to Processing. The Pixy cam is picking up color “signatures” I have set and drawing rectangles around each one, and giving me information for each color detected (how many colors are detected (as blocks), which block, its signature, x, y, width, height, index and age)

My goal is to create an interactive / generative piece that involves a hula hoop I have wrapped in different colors of tape. Basically, I would like to use the x, y, width and height data to generate imagery. For now, I’d really just like to start simple and make an ellipse appear for every time a color, or block, is recognized.

Please see below for reference to what is coming in through the serial port to the console:

I need to know how to pull the numbers and use them. We tried splitToken with “:” but it didn’t work out very well, because it was also pulling the text out.

Here’s an example of what the Pixy cam is doing.

PROCESSING CODE:

import processing.serial.*;
Serial myPort;  // The serial port
Boolean firstContact = false;

void setup(){
myPort = new Serial(this, Serial.list()[2], 115200);
}

void draw(){

}

void serialEvent( Serial myPort) {
  //println("serial event");
  //put the incoming data into a String - 
  //the '\n' is our end delimiter indicating the end of a complete packet
  String val = myPort.readStringUntil('\n');
  //make sure our data isn't empty before continuing
  if (val != null) {
    //trim whitespace and formatting characters (like carriage return)
    val = trim(val);
    println(val);

    //look for our 'A' string to start the handshake
    //if it's there, clear the buffer, and send a request for data
    if (firstContact == false) {
      if (val.equals("A")) {
        myPort.clear();
        firstContact = true;
        myPort.write("A");
        println("contact");
      }
    } else { //if we've already established contact, keep getting and parsing data
      println(val);

      if (mousePressed == true) 
      {                           //if we clicked in the window
        myPort.write('1');        //send a 1
        println("1");
      }

      // when you've parsed the data you have, ask for more:
      myPort.write("A");
    }
  }
}

#2

Hi mck.sanderson,

First can you please format your code?
You can see how to do it here: Guidelines—Tips on Asking Questions

Also, try to avoid posting picture of data because we can’t copy/paste them to use them.

Now for your question, splitToken should work nice in your case. You can split using the space as the delimiter and just take the value 3, 5, 7, 9, 11, 13, 15

Try the code below:

void setup() {
  String myText = "block 0: sig: 1 x: 252 y: 134 width: 8 height: 3 index: 209 age: 8";
  String[] splitText = splitTokens(myText, " "); 
  println(splitText[3], splitText[5], splitText[7], splitText[9], splitText[11], splitText[13], splitText[15]);
}

#3

Thank you so much, jb4x!! I apologize for the super late response – I’ve been working hard over here. Your suggestion helped me so much! I tried splitTokens before with “:” and for some reason didn’t even think about using spaces.

My new code (see below) is working pretty well. Now, I think I’m ready to go onto more complex stuff. But, I’m really pleased with where I am right now.

Like I said, I’m a beginner, so I’m commenting the heck out of this so I don’t forget how things are working the way they are…

import processing.serial.*; //// import data from serial
Serial myPort;  //// defines the serial port
Boolean firstContact = false;
float block; //// defines a global float for "block", and other values from the port below..
float sig;
float xpos;
float ypos;
float blockWidth;
float blockHeight;
float age;

void setup() {
  myPort = new Serial(this, Serial.list()[2], 115200); //// tell myPort to call from Port2
  background(0);
  size(800,800);
}

void draw() {
 //fill(random(255),random(255),random(255));
 fill(0);
 stroke(random(255),random(255),random(255)); //// colors the stroke from full spectrum of RGB values
 strokeWeight(random(255)); //// varies the stroke weight randomly
 ellipse(xpos,ypos,blockWidth,blockWidth); //// draws an ellipse using the floats I defined globally
 fill(0,20); //// draws a translucent rectangle the size of the screen to cause a fade effect
 noStroke();
 rect(0,0,width,height);
}

void serialEvent( Serial myPort) {
  ////put the incoming data into a String - 
  ////the '\n' is our end delimiter indicating the end of a complete packet
  String val = myPort.readStringUntil('\n'); // val = all info coming in thru the port
  ////make sure our data isn't empty before continuing
  if (val != null) {
    ////trim whitespace and formatting characters (like carriage return)
    val = trim(val);
    //println(val);
    //val = String(val);
    String[] splitText = splitTokens(val, " "); // splits val at each "space," creating an array

    //// the exact contents of the string array, splitText:
    //// splitText[15] = [0]="block:", [1]=block, [2]="sig:", [3]=sig, [4]="x:", [5]=x,
    //// [6]="y:", [7]=y, [8]="width:", [9]=width, [10]="height:", [11]=height, [12]="index:",
    //// [13]=index, [14]="age:", [15]=age
    
    if(splitText.length > 15) //// ONLY if the serial port reports a FULL string of data, then...
    {                         //// print certain items in the array to the console:
    println("block = " + splitText[1] + "\n" + "sig = " + splitText[3] +
    "\n" + "x = " + splitText[5] + "\n" + "y = " + splitText[7] +
    "\n" + "width = " + splitText[9] + "\n" + "height = " + splitText[11] +
    "\n" + "age = " + splitText[15]);
    
    //// and turn each string of numbers into a float:
    block = float(splitText[1]); ////the block value currently still includes ":" so I need to fix that..
    sig = float(splitText[3]);
    //xpos = float(splitText[5]);
    //ypos = float(splitText[7]);
    //// and map the x and y positions to the width and height of the canvas
    xpos = map(float(splitText[5]),0,319,0,width); //// the hightest x value from Pixy is 319, lowest = 0
    ypos = map(float(splitText[7]),0,199,0,height); //// the hightest y value from Pixy is 199, lowest = 0
    blockWidth = float(splitText[9]);
    blockHeight = float(splitText[11]);
    age = float(splitText[15]);
  }
    
    //println("split version" + splitText);

    //// look for our 'A' string to start the handshake
    //// if it's there, clear the buffer, and send a request for data
    if (firstContact == false) {
      if (val.equals("A")) {
        myPort.clear();
        firstContact = true;
        myPort.write("A");
        //println("contact");  
      }
    } else { //if we've already established contact, keep getting and parsing data
      //println(val);

      // when you've parsed the data you have, ask for more:
      myPort.write("A");
    }
  }
}

#4

Happy to read that you manage!


#5

Yes, congratulations on solving your problem and many thanks for sharing your solution with the forum. That is really helpful to other learners.