Read Serial value in Processing. [SOLVED]

Hello!

I am learning about processing and would like to use my HC-SR04 ping sensor values to move a “spray” along the y axis. I would get the distance from the PING sensor, and use that distance to set the Y Axis of a point in Processing.

Here’s the Processing code (I want the PING sensor distance to replace the ‘mouseY’ part), which is mainly from this book, ‘random particle spray’

/*
Main code, and inspiration, from Ira Greenberg
Chapter 6 "Processing, Creative Coding and Computational Art, random particle spray
*/

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


Serial myPort; // create object from Serial class
String val; // data rec'd from the Serial port
// int intVal; // for converting the VAL string to INT
// int mapVal; // set up a value for the mapped val, will map the readable distance of 0 to 255 to 0 to Window Height

void setup(){
  size(800,500);
  background(0);
  stroke(255);
  colorMode(HSB,255);
  strokeWeight(5);
  frameRate(10);
  boolean keypress;

  // String portName = Serial.list()[4];
  myPort = new Serial(this,"COM5", 9600);

}

void splatter(int totalPts, float randSmall, float randLarge, int strkWeight, boolean randomOnOff){
  strokeWeight(strkWeight);
  background(0);
   //  int totalPts = 500;
  float steps = totalPts +1;
  float rand = 0;
  float k,l;
  float colorl;
  float mouseYPos;
  boolean keypress = false;
  for (int i = 1; i < steps; i++){
    mouseYPos = mouseY-(height/2);
    if (mousePressed){
      rand += random(-randSmall,randSmall);
      k = (width/steps)*i;
      l = (height/2)+random(-rand,rand);
      colorl = map(l,0,height,0,255);
      if (randomOnOff){
      rand+= random(-randLarge,randLarge);
      } else {
        rand+= randLarge;
      }
      stroke(colorl,l,255);
      point(k,l+mouseYPos);
    } else {
      if (randomOnOff){
      rand += random(-randSmall,randSmall);
      } else {
        rand+= randSmall;
      }
      k = (width/steps)*i;
      l = (height/2)+random(-rand,rand);
      colorl = map(k,0,height,0,255);
      stroke(k,colorl,255);
      point(k,l+mouseYPos);
    }
  }
    if (key == 't'){
    keypress = true;
  } else if (key == 'f'){
    keypress = false;
  }
}



void distancePrint(){               // Here's where my code is to get the distance, via Serial, and will insert the code to make this the
  if (myPort.available()>0){         //  y Axis info.
   val = myPort.readString();
  }
  int intVal = Integer.parseInt(val);
  println(intVal);
  // float mapVal = map(intVal, 0, 255, 0, height);
  // println(mapVal);
}


void draw(){
boolean keypress = false;


 distancePrint();
 // splatter(500, .2, .5, 5, keypress); // total points, random small, random large, stroke size, random on [true] or off [false]

}

I am able to get the Arduino to send the distance, in CM, to the Serial monitor. Here’s that code:

#include <NewPing.h>

#define TRIGGER_PIN  10  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     9  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 500 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

Servo myservo;
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 

void setup() {
  Serial.begin(9600); 
}

void loop() {
  // read distance from sensor and send to serial
  getDistanceAndSend2Serial();
  
}

int getDistanceAndSend2Serial() {
  int cm = sonar.ping_cm();
//  Serial.print("Distance in CM: ");
  Serial.println(cm);
  
}

So, first, in the processing code, I believe I will need to map() the sensor distance and do something like:

  // float mapVal = map(intVal, 0, 255, 0, height);
  // println(mapVal);

But first, I need to get the String val to an integer or float before I can map it. I have tried this, and it works for maybe one seconds, then seems to stop reading…

  float intVal = Float.parseFloat(val);
  println(intVal);

I can post the errors I’m getting if you like, but I think my problem/issue is reading the Serial return (which is stored as a String, turning that into a Float that I can map to the window height (that way, if the distance is over 500 in cm, it maxes out at 500).

Thanks for any help - and if this is convoluted/not clear, let me know and I can try to clarify!

Why is the function for reading from the serial port called distancePrint()? Use a meaningful name. What would you think if the function for reading from a digital pin was called readyAimFire()? It wouldn’t make any sense, would it?

You are reading all the serial data available, regardless of whether it is a partial packet, a full packet, or multiple packets. You really need to use the Serial::bufferUntil() method in setup(), the Serial.readString() or Serial::readBytesUntil() methods in serialEvent(), and you really need to implement the serialEvent() method.

Once you have a String, you should use String::trim() to get rid of white space, and String::split() to split the string into tokens. One of the interesting aspects of split() is that it will convert the tokens to an array of ints or floats. It matters not that there is only one token, and that the array contains only one element.

PaulS:
Why is the function for reading from the serial port called distancePrint()? Use a meaningful name. What would you think if the function for reading from a digital pin was called readyAimFire()? It wouldn't make any sense, would it?

Yeah, good call. I changed to "distanceRead".

You are reading all the serial data available, regardless of whether it is a partial packet, a full packet, or multiple packets. You really need to use the Serial::bufferUntil() method in setup(), the Serial.readString() or Serial::readBytesUntil() methods in serialEvent(), and you really need to implement the serialEvent() method.

Okay - I have looked that up and will see how to use it. I would essentially be replacing the distanceRead(){ } with SerialEvent(Serial myPort){} ?

and noted about the string parsing. I'll try some stuff and come back with any questions. Thanks very much!

I would essentially be replacing the distanceRead(){ } with SerialEvent(Serial myPort){} ?

It's serialEvent(), not SerialEvent(), and the argument is the port that the data arrived on. This matters since only one serialEvent function is defined, but called when data arrives on any serial port, in cases where you have multiple serial ports open. I mention this only because the argument will mask the global variable of the same name, which is generally a bad idea. So, the argument name should NOT be myPort.

Using serialEvent() is only one of the three steps needed to properly read serial data in Processing. You need to use the bufferUntil() method to define when serialEvent() should be called, and you need to read all the data available when the function is called. And, of course, deal with that data properly.

Aha! I have it working!

Here's the Processing code I got to work (note: it's shortened to just include the pertinent parts that I fixed):

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


Serial myPort; // create object from Serial class
String val; // data rec'd from the Serial port
// int intVal; // for converting the VAL string to INT
// int mapVal; // set up a value for the mapped val, will map the readable distance of 0 to 255 to 0 to Window Height
String comPortString; // the String received from the Arduino



void setup(){
  size(800,500);
  background(0);
  stroke(255);
  colorMode(HSB,255);
  strokeWeight(5);
  frameRate(10);
  boolean keypress;

  // String portName = Serial.list()[4];
  myPort = new Serial(this,"COM5", 9600);
  myPort.bufferUntil('\n');

}


void serialEvent(Serial cPort){
  comPortString = cPort.readStringUntil('\n');
    if (comPortString!= null){
      comPortString = trim(comPortString);
    }
//    println(comPortString);
    float comPortInt = float(comPortString);
    println(comPortInt);
}

Thanks a lot PaulS for your direction and help! Now, to work on mapping that and getting it to work with the visuals. :grin: