Processing

From Mech
Jump to navigationJump to search

Overview

Processing is an open source programming language and IDE based on Java. It is free, works on PC/Mac/Linux, and is easy to learn. Processing lets the programmer quickly make visual objects and interactive programs. It is also easy to use Processing to communicate with a serial port, so programs can interact with microcontrollers. This wiki page will describe how to get Processing, create a simple program, open a serial port, and use an external library to create a GUI (graphical user interface). At the end you will find a template for a Processing program that is ready to communicate over serial and interface with GUI objects.

Download

Processing can be found here. Download, unzip and install Processing. This will create two important folders. The first is the folder you have unzipped. It contains the actual Processing program. The second folder is something like Documents/Processing, depending on your platform. This folder is where you store your projects and add libraries.

Resources

Download Processing

Processing Basics

Processing Language Reference

Serial Library

Internal and External Libraries

GUI Library

ProcessingPIC32Communication

Processing comes with a large number of great example projects. Go to File->Examples to see example code specific to making 3D objects, Basics, Libraries and Topics. The Processing website also contains example code and links to other projects that may contain examples.

A Simple Program

Processing IDE

Processing is designed to allow people who are unfamiliar with programming to quickly get up and running making visually compelling programs.

A Processing file is called a sketch. The IDE is shown at the right. A sketch is saved as a .pde in its own folder with the same name and opens in a tabbed format.


A simple Processing sketch

A simple Processing sketch is shown at the right. The IDE has several icons at the top which allow you to run your code, stop running code, save and open code, and export your project. At the bottom of the IDE is a debugging window that you can write to with the print() and println() functions. Processing is object oriented, which basically means that all functions are data structures, and all of your code will run in functions.

All Processing code has at least two major functions, setup() and draw().

setup() is the first function to run. In it you define the size of the window that Processing opens, how fast it updates, and initialize any variables or other functions. setup() runs only once.

draw() runs after setup(). draw() will be called a certain number of times a second, as defined in frameRate() in setup(). This means that draw() functions as an infinite loop. Typically you use draw() to update your graphics. You can also use draw() to check the status of variables, the mouse position, and other objects.


The sketch above contains the following code:

/*
Nick Marchuk
02/23/2010
The basic form of a Processing sketch
*/

// global variables
int x, y;

// setup() function
// this function is the first part of the code to run
// use it to setup propertie of the program and initialize variables
void setup() {
  size(400,400); // size(x,y) of the window in pixels
  frameRate(30); // call the draw() function at 30 frames per second to update the window
  background(30,30,220); // set the background color of the window in (red, green, blue), see Tools->Color Selector
  x = y = 0;
  println("Program Started!"); // print some text to the debug window
}

// draw() function
// this function acts as your infinite loop, running as often as defined in frameRate in setup()
void draw() {
  x = mouseX;
  y = mouseY;
  println("cursor at: " + x + ", " + y);
}

Note the sections used:

  • An area of comments stating who made the code and when, and the purpose of the code
  • Global variables. Remember that variables declared in a function, like in setup() or draw(), are local and their values will not be remembered between multiple calls of the function, so global variables will be needed, but try not to use too many, their use is not good programming style. They will eat up your memory and can get confusing. It is much better to pass variables between functions instead of using globals if at all possible.
  • void setup(). This function runs first and only once. We use it to:
    • set the size of the window in pixels
    • set the frameRate, how many times draw() will be called per second
    • set the background color of the window, in rgb
    • initialize the global variables x and y
    • write some text to the debug window to let us know the program is running
  • void draw(). In this case when draw() is called we put the value of the mouse position into the variables x and y and print them to the debug window.

Running this sketch will produce a blue window and a stream of text in the debug window. Hit the escape key or the stop button to end the program.

Another Simple Program

Lets edit the simple program and add some objects. Lets declare a function that will take the position of the mouse and change the color and radius of a circle and inversely change the background color of the window. Lets also move the circle back and forth across the screen.

/*
Nick Marchuk
02/23/2010
Another basic sketch, demonstrating how easy it is to program in Processing
*/

// global variables
//int x, y; // lets make them local to draw() instead of global

// setup() function
void setup() {
  size(400,400); // size(x,y) of the window in pixels, stored automatically in the parameters width and height
  frameRate(30); // call the draw() function at 30 frames per second to update the window
  background(128); // set the background color of the window, one number instead of 3 means the color will go from black->white as 0->255
  println("Program Started!"); // print some text to the debug window
}

// draw() function
void draw() {
  // get the mouse position
  int x = mouseX;
  int y = mouseY;
  println("cursor at: " + x + ", " + y);
  
  // call some functions to get the circle size, color and position
  int circle_rad = get_circle_rad(x,y);
  int circle_color = get_circle_color(x,y);
  int background_color = 255 - circle_color; // invert the color for the background
  background(background_color); // apply the color to the background
  int circle_x = get_circle_pos();
  
  // draw the circle
  fill(circle_color); // objects have no color, instead they are drawn with the color currently in fill, so all objects after this fill() will be drawn with this color
  ellipse(circle_x,height/2,circle_rad,circle_rad);
}

// send this function the position of the mouse and return what the radius should be
int get_circle_rad(int x, int y) {
  int rad = int(0.25*((2*x-y/2)^2)); // some arbitrary fn, must output an int but ^ works on floats
  return rad;
}

// send this function the position of the mouse and return the color the circle should be
int get_circle_color(int x, int y) {
  int c_color = int(map(x+y,0,width+height,0,255)); // some arbitrary fn, new number as float = map(current number,min current number, max current number, min new number, max new number)
  return c_color;
}

// return the x position of the circle
int get_circle_pos() {
  float m = millis(); // how many milliseconds have passed since the program started
  int c_x = int(width*abs(sin(2*PI*1/4000*m))); // make the cirle move back and forth
  return c_x;
}

The result looks like:

Another simple program in Processing


Serial Port Communication with Processing

To get Processing to interact with a microcontroller, we will open a serial port and read and write to it. What you read and write will depend on what you plan to do. This section will describe how to see the available comm ports, initialize one, write to it and read what comes in.

To use serial communication you need to include the serial library in your sketch. This library comes with Processing, all you need to do is add it to your sketch by selecting Sketch->Import Library...->Serial I/O. This will add the line

import processing.serial.*;

to the top of your code. You could manually type this in as well.

The serial library contains the data type Serial, which allows you to set the baud, flow control, and writing. A serial Event is created when Processing receives data, allowing you to read.

The following code will initialize a serial comm port, write and read.

// open a serial port
// expects a stream of input strings, in the form of "# #\n"
// so send it a number followed by a space followed by a number and a newline from your microcontroller

// include the serial library
import processing.serial.*;

// make some Serial objects 
int numPorts = 1; // how many serial ports you want to use in this sketch
int numPortInList = 0; // the port you want to use, not the comm number but which one in the list made in the initSerial() function
int numPortUsing = 0; // which port you refer to, if you opened more than one
Serial myPorts[] = new Serial[numPorts]; // an array of serial objects

// initializations
void setup() {
  size(300,300);
  frameRate(30);
  println("Program started");
  initSerial(); // call a user written function to initialize the serial port, if there are none then exit the program
}

// infinite loop
void draw() {
  // your program here 
  
  // if the mouse is pressed, write the x and y position of the mouse out of the port
  // if you loop the serial tx to its rx as a test, this code will read the x y position and print it to the debug window
  if (mousePressed == true) {
    println("mouse pressed");
    myPorts[numPortUsing].write(mouseX + " " + mouseY + "\n");
  }
}

// initialize the serial port
void initSerial() {
  println(Serial.list()); // List all the available serial ports in the debug window

  // see how many ports there are, if there aren't any then exit
  if (Serial.list().length > 0) {

    String portList = Serial.list()[numPortInList]; // grab the name of the port you want to use
    myPorts[numPortUsing] = new Serial(this, portList, 19200); // initialize the serial port with a baud rate of 19200

    // read bytes into a buffer until you get a linefeed (ASCII 10):
    myPorts[numPortUsing].bufferUntil('\n');
  }
  else { // uh oh, no ports, exit the program
    println("No serial ports found!");
    exit();
  }
}

// serial event. This event is triggered when bytes appear in the serial port buffer
// check which port generated the event, just in case there are more than 1 ports talking
// this code expects to see a string with a number, a space, a number and a newline
// it extracts the two number from the string and prints them to the debug window
void serialEvent(Serial thisPort) { 
  // variable to hold the number of the port
  int portNumber = -1;

  // iterate over the list of ports opened, and match the one that generated this event
  for (int p = 0; p < myPorts.length; p++) {
    if (thisPort == myPorts[p]) {
      portNumber = p;
    }
  }

  // read the serial buffer as a string until a newline appears
  String myString = thisPort.readStringUntil('\n');

  // if you got any bytes other than the newline
  if (myString != null) {

    myString = trim(myString); // ditch the newline

    // split the string at the spaces, save as integers, can't do floats
    int dataFromSerial[] = int(split(myString, ' '));

    // check to make sure its the right port and there are the correct number of integers in the packet
    if ((dataFromSerial.length == 2)&&(portNumber==numPortUsing)) {
      // print what it got in the debug window
      print(dataFromSerial[0]);
      print(" ");
      println(dataFromSerial[1]);
    }
  }
} // end serialEvent

After loading the code and pressing the mouse a few times, the debug window looks like:

The debug window after running the serial sketch


A Graphical User Interface (GUI) with Processing

A GUI contains elements like check boxes, radio buttons, regular buttons, labels and text entry. While it is possible to write your own custom objects to perform the function objects found in a GUI (see the Examples->Topics->GUI->Button example), it would be even better to use a library that contains all of these elements. Processing does not come with a library of GUI objects, but there are several external libraries created by labs and individuals around the world with GUI objects. Check the external libraries available here (scroll down to see them).

We will use the Graphic Interface library controlP5. Download the associated .zip file and unzip in a folder called libraries in the folder that contains your project folder. (Your file structure should look like:

  • My Documents
    • Processing
      • MyProject
        • MyProject.pde
      • libraries
        • controlP5

All external libraries should be put in this libraries folder)

After you have added this folder, you should be able to open Processing and add the library to your sketch by going to Sketch->Import Library...->controlP5, or typing

import controlP5.*;

at the top of your sketch.

This library comes with some great example sketches that show off the different objects it provides. Look under my docs/Processing/libraries/controlP5/examples for 40 different example sketches demonstrating the use of individual GUI objects.

The following sketch uses a bunch of common GUI objects to demonstrate the library:

// Nick Marchuk
// 3/5/2010
// Show off GUI basics with the controlP5 external library
// the best help section is at http://www.sojamo.de/libraries/controlP5/reference/index.html?controlP5/package-summary.html, click on index
// you must create a "libraries" folder in the folder where you have your processing files (c://...documents/processing) and unzip the controlP5 folder there to access the library

import controlP5.*; // controlP5 GUI library

ControlP5 controlP5; // create the handler to allow for controlP5 objects

// not required to define each gui object you want, but useful for text objects, makes them easier to access to change their text
Textlabel myTextlabelA; // create a text label, an area of text independent of anything else
Textarea myTextarea; // a multiline textarea with scrollbar
ListBox myListbox; // a list that generates an event when you click on an element in the list
ControlFont font; // a unique font to be applied to an object

int myColorBackground = color(0,0,0); // background color of window

public int numberboxValue = 100; // initial value of numberboxValue, will change on the fly as the numberbox is changed (because it has the same name as the numberbox that will be declared)

void setup() {
  size(600,400);
  
  controlP5 = new ControlP5(this); // initialize the GUI controls
  
  // print all of the possible fonts that you can use, uncomment to print the big list
  //println(PFont.list());
  
  // create the font for a specific gui object
  font = new ControlFont(createFont("Arial",20),14); // ControlFont(the font, font size)
  font.setSmooth(true);
  
  // set the font for all of the other gui objects -> this messes up the text entered in the text field, text typed in is correct but only the last character is shown in the field
  controlP5.setControlFont(new ControlFont(createFont("Comic Sans MS",20), 12));  // ControlFont(the font, font size)

  // a slider graphically shows its value in relation to its min and max
  controlP5.addSlider("slider1",100,167,128,100,250,20,100); // slider(name,min,max,default,x,y,width,height)
  controlP5.addSlider("slider2",0,  255,128,150,250,100,10); // slider(name,min,max,default,x,y,width,height)
  
  // a button press will trigger the function with the same name as the button
  controlP5.addButton("buttonA",0,100,100,80,19); // buton(name,value,x,y,width,height)
  controlP5.controller("buttonA").setCaptionLabel("Button A"); // change what the button says on it
  
  // set the font and font size of the button
  controlP5.controller("buttonA").captionLabel().setControlFont(font);
  controlP5.controller("buttonA").captionLabel().setControlFontSize(15);
  controlP5.controller("buttonA").captionLabel().toUpperCase(false);
  
  // declare the text label this way to make it easier to chage the text in it
  myTextlabelA = controlP5.addTextlabel("textlabelA","hi there",5,5); // textlabel(name,text,x,y)
  
  // a bang is a lot like a button, but simpler
  controlP5.addBang("bang",60,20,45,45).setTriggerEvent(Bang.RELEASE); //addBang(name,x,y,width,height)
  controlP5.controller("bang").setLabel("a_bang");
  
  // a numberbox is like a slider but without the graphics
  controlP5.addNumberbox("numberboxValue",128,100,130,100,14); // addNumberbox(name,default,x,y,width,height)
  
  // a textarea is a set of text, like a multiline textlabel, can have a scroll bar
  // define like this to make changes easier
  myTextarea = controlP5.addTextarea( // addTextarea(name,text,x,y,width,height)
  "label1textarea", 
  "the first line of text \n"+ // \n is newline, works but controlP5 doesn't particularly like it, spits out a warning in the debug window
    "another line of text \n"+
    "hint for the GUI objects in controlP5: use ALT + mouseDown to move them around the screen if you don't like their placement.", 
  375,100,200,60);
  
  // a textfield is for text entry
  // after typing an enter the function textValue is triggered, putting the entered text into the Textarea, as set in the function textValue() below
  controlP5.addTextfield("textValue",100,170,200,20); // addTextfield(name,x,y,width,height)
  
  // a listbox is like a dropdown
  myListbox = controlP5.addListBox("myList",350,200,120,120); //addListBox(name,x,y,width,height)
  myListbox.captionLabel().toUpperCase(false); // dont force the caption text to be capitalized. Most object have lots of little fns like this, check the examples / online help
  myListbox.captionLabel().set("Listbox label");
  for(int i=0;i<6;i++) {
    myListbox.addItem("item "+i,i); // addItem(name,value)
  }
}

// inifinite loop, doesn't do much because events are triggered when the GUI objects are used, we don't have to check them automatically
void draw() {
  background(myColorBackground);
}

// run this when slider1 is triggered
void slider1(float theColor) { // theColor is the value of the slider when it is triggered
  myColorBackground = color(theColor);
}

// run this when slider2 is triggered
void slider2(float theValue) { // theValue is the value of the slider when it is triggered
  myTextlabelA.setValue("slider2 = "+theValue);
}

// run this when buttonA is triggered
public void buttonA(int theValue) { // theValue is the value of the button when it is triggered (not to interesting in the case of a button)
  myTextlabelA.setValue("button pushed");
}

// run this when bang is triggered
void bang() {
  int theColor = (int)random(255);
  myColorBackground = color(theColor);
}

// run this when there is an enter in the textfield
public void textValue(String theText) { // theText is the what was entered in the textfield
  myTextarea.setText(theText);
}

// print the name of the control being triggered, for debugging purposes or to see if the event was from the Listbox
public void controlEvent(ControlEvent theEvent) {
  // ListBox is of type ControlGroup (a group of GUI objects instead of a single object), you need to check its theEvent with if(theEvent.isGroup())to avoid an error message from controlP5
  if (theEvent.isGroup()) {
    // an event from a group
    if (theEvent.name()=="myList") {
     println("got myList"+"   value = "+theEvent.group().value()); 
    }
  }
  else {
    //println(theEvent.controller().name()); // this is for debugging purposes, see what GUI object was triggered
  }
}

The code above creates the following GUI:

A variety of GUI objects available in the controlP5 library

Two notes about these GUI objects:

  • Holding the ALT key and clicking on an object allows you to drag it around the screen. If you print the location of the object to the debug window when it is triggered, you can initially place the object anywhere and note the position you like after moving it and changing your code to match
  • You can declare your object at the top of your code like "Textarea myTextarea;" and use "myTextarea = controlP5.addTextarea(..." in setup() or directly use "controlP5.addButton("buttonA",0,100,100,80,19);" in setup() to add the object. The former method is better for objects that you want to change dynamically, like the text in a textArea. The later method is good for objects that you will not need to change after they are initialized, like buttons.


A Template for a GUI using Serial Communication in Processing

This section will describe a template for Processing projects that use a GUI interface and serial communication. It is often a challenge to share code between PCs when the serial port is hard coded for one PC because the name and number of comm ports differs on every PC, particularly when the computer has bluetooth. This template attempts to solve this problem by listing all of the available comm ports and allowing the user to select the port they wish to use when the sketch loads. It contains all of the framework necessary to setup the serial communication and use common GUI objects.

// Nick Marchuk
// 2/25/2010
// Template for serial communication and a GUI
// Puts all available comm ports in a list, initializes the one that is clicked, text entry for output and text area for input

// serial variables
import processing.serial.*; // serial library
Serial[] myPorts = new Serial[1]; // lets only use one port in this sketch

// GUI variables
import controlP5.*; // controlP5 library
ControlP5 controlP5; // create the handler to allow for controlP5 items
Textlabel txtlblWhichcom; // text label displaying which comm port is being used
Textarea commTextarea; // text area displaying what has been received on the serial port
ListBox commListbox; // list of available comm ports

// setup
void setup() {
  size(600,400);
  frameRate(30);

  controlP5 = new ControlP5(this); // initialize the GUI controls

    println(Serial.list()); // print the comm ports to the debug window for debugging purposes

  // make a listbox and populate it with the available comm ports
  commListbox = controlP5.addListBox("myList",5,25,120,120); //addListBox(name,x,y,width,height)
  commListbox.captionLabel().toUpperCase(false);
  commListbox.captionLabel().set("Listbox label");
  for(int i=0;i<Serial.list().length;i++) {
    commListbox.addItem("port: "+Serial.list()[i],i); // addItem(name,value)
  }

  // text label for which comm port selected
  txtlblWhichcom = controlP5.addTextlabel("txtlblWhichcom","No Port Selected",150,25); // textlabel(name,text,x,y)

  // when triggered, write the text over the serial line
  controlP5.addTextfield("EnterTextToSend",5,170,200,20); // addTextfield(name,x,y,width,height)

  // when new info comes into the serial port, write it to this text area
  commTextarea = controlP5.addTextarea( // addTextarea(name,text,x,y,width,height)
  "label1textarea", 
  "No data yet...", 
  225,170,200,30);
  commTextarea.setColorBackground(0xffff0000);
  
  // a button to send the letter a
  controlP5.addButton("Send_A",1,5,210,80,19); // buton(name,value,x,y,width,height)
}

// infinite loop
void draw() {
  background(128);
}

// print the name of the control being triggered (for debugging) and see if it was a Listbox event
public void controlEvent(ControlEvent theEvent) {
  // ListBox is if type ControlGroup, you need to check the Event with if (theEvent.isGroup())to avoid an error message from controlP5
  if (theEvent.isGroup()) {
    // an event from a group
    if (theEvent.name()=="myList") {
      InitSerial(theEvent.group().value()); // initialize the serial port selected
      //println("got myList"+"   value = "+theEvent.group().value()); // for debugging
    }
  }
  else {
    //println(theEvent.controller().name()); // for debugging
  }
}

// run this when there is an enter in the textfield
public void EnterTextToSend(String theText) {
  myPorts[0].write(theText+"\n"); // write the text in the field
  println("sent "+theText); // print it to the debug screen as well
}

// run this when buttonA is triggered, send an a
public void Send_A(int theValue) { 
  myPorts[0].write("a\n"); // write an a
  println("sent a"); // print it to the debug screen as well
}

// initialize the serial port selected in the listBox
void InitSerial(float portValue) {
  println("initializing serial " + int(portValue) + " in serial.list()"); // for debugging

  String portPos = Serial.list()[int(portValue)]; // grab the name of the serial port 
  
  txtlblWhichcom.setValue("COM Initialized = " + portPos);
  
  myPorts[0] = new Serial(this, portPos, 19200); // initialize the port

  // read bytes into a buffer until you get a linefeed (ASCII 10):
  myPorts[0].bufferUntil('\n');
  println("done init serial");
}

// serial event, check which port generated the event 
// just in case there are more than 1 ports open
void serialEvent(Serial thisPort) { 
  // variable to hold the number of the port:
  int portNumber = -1;

  // iterate over the list of ports opened, and match the 
  // one that generated this event:
  for (int p = 0; p < myPorts.length; p++) {
    if (thisPort == myPorts[p]) {
      portNumber = p;
    }
  }

  // read the serial buffer until a newline appears
  String myString = thisPort.readStringUntil('\n');
  myString = trim(myString); // ditch the newline
  println("got: " + myString); // print to debug window
  commTextarea.setText(myString); // put it in the text area
  
  // uncomment the following if you are getting streaming packets of data that need to be parsed
/*
  // if you got any bytes other than the newline
  if (myString != null) {

    //myString = trim(myString); // ditch the newline

    // split the string at the spaces, save as integers
    int sensors[] = int(split(myString, ' '));

    // convert to x and y or whatever
    if ((sensors.length == 2)&&(portNumber==0)) { // hardcoded portNumber==0 because only using one port in this sketch
      //float x = sensors[0]/100.0; // whatever conversion you need to do
      //float y = sensors[1]/100.0;
    }
  }
*/  
} // end serialEvent

This sketch creates the following window:

A template for a Processing sketch using serial communication and a GUI


A New Serial Library with Hardware Flow Control for Processing

The Processing serial library does not have an argument to enable Hardware Flow Control, although standard Java does. If you replace the default serial library with a slightly edited one, you can use Hardware Flow Control, and only one line of code changes!

Open your Processing folder (where the Processing executable is), and go to \modes\java\libraries, and delete the serial folder. Replace it with this serial folder: Media:serial.zip

This new serial library behaves exactly the same as the old library, except with an additional argument when opening a port. For example:

myPort = new Serial(this, portName, 1500000, 'N', 8,1,1);

where

  • 1500000 is the baud
  • 'N' means no parity
  • 8 means 8 bit data
  • The first 1 means 1 stop bit
  • And the last 1 is the new argument - 1 to enable hardware flow control, and 0 to disable

If you open a port without specifying all of the arguments, for example

myPort = new Serial(this, portName, 9600);

Hardware Flow Control will not be used - **Hardware Flow Control is off by default***

You must specify all of the arguments to enable Hardware Flow Control.