Two-way Serial Communication Using an Arduino and Processing

What You Will Need to Know

To get the most out of this lab, you should be familiar with how to program an Arduino, and with the basics of serial communication. If you’re not, review the links below:

Arduino Programming Basics
Arduino Analog Input
Serial Output from Arduino to Processing

Items You Will Need

You will need the hardware listed below. For the analog sensors, you can use photocells, potentiometers or any sensor that will give you analog signals.

The breadboard, schematics and Arduino sketches for this lab activity are in the course code repository on Github, in this folder.

The Processing sketches for this lab activity are in the course code repository on Github, in this folder.

Connect the Sensors

Build the circuit below.

Analog_sensors_Button_bb
Analog_sensors_Button_schem

Sending Values for Multiple Sensors

In the previous serial lab, you sent data representing one sensor’s value, over and over. When sending multiple sensor values, things get a little more complicated. You must devise a way to know which value represents which sensor. For example, if you used the following loop to send your sensor values:

void loop() {
   for (int thisSensor = 0; thisSensor < 3; thisSensor++) {
      int sensorValue = analogRead(thisSensor);
      Serial.print(sensorValue);
      Serial.print(",");
   }
}

Your output would look something like this:

342,345,406,334,337,407,398

There is no way to determine which value corresponds to which sensor (the numbers just keep printing to the right without a a newline). We need to get the sensor values in order. Essentially, this entails coming up with your own protocol of sorts. Some kind of format that both the sender and receiver agree upon ahead of time.You can accomplish in two basic ways: you can use punctuation or you can use handshaking (aka “call and response”).

Punctuation

One way to send the data such that it can be interpreted clearly is to punctuate each set of data uniquely. Just as a sentence ends with a period, you can end your data with a carriage return and a newline. Using the two photocells and the button you wired up, we will send the data in sets of three — separated by a comma and ending ina new line & carriage return. Here is some code that will read the state of all three and send them out the serial port, properly formatted. Upload to your Arduino and observe.

const int btnPin = 2; // digital input

void setup() {
  // start serial port at 9600 bps
  Serial.begin(9600);
  // configure the digital input
  pinMode(btnPin, INPUT);
}

void loop() {
  // read the analog sensor
  int sensorVal = analogRead(A0);
  // print the results
  Serial.print(sensorValue);
  Serial.print(",");

  // read the analog sensor
  sensorVal = analogRead(A1);
  // print the results
  Serial.print(sensorValue);
  Serial.print(",");
  
  // read the button state
  sensorValue = digitalRead(btnPin);
  // print the results:
  Serial.println(sensorValue);
}

This code can also be found here.

Now each grouping of three readings is separated by a newline, so whenever you get a newline, you know that the next value is the first sensor. Meanwhile each individual sensor reading is separated by a comma.

Receive the Data in Processing

Now we must write a Processing sketch that reads and parses the data sent by the Arduino program above. The code is similar to the program from the previous serial lab (SensorGraphASCII), but instead of one sensor we have to parse out three and use that data not to build a graph but to control the position and color of a shape on the screen. Run this code in Processing and observe.

/*
 * AnalogSensorsButton
 *
 * Carlos Castellanos
 * August 5, 2020
 *
 * Example of serial communication between Processing & Arduino using the
 * "punctuation" method
 * Arduino sends the data for three sensors as ASCII and Processing
 * uses that data not to control the position and color of a shape on the screen.
 * 
 *
 */


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

Serial myPort;              // The serial port

float fgcolor = 0;          // Fill color defaults to black
float xpos, ypos;           // Starting position of the ball
 
void setup() {
  size(800, 600);
  
  // List all the available serial ports
  println(Serial.list());
  
  // Change the number in the Serial.list() array to the appropriate
  // number of the serial port that your microcontroller is attached to.
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 9600);
  
  // don't generate a serialEvent() until you get an ASCII newline character
  myPort.bufferUntil('\n');
}

void draw() {
  background(#2b9468); // green background
  fill(fgcolor);
  
  // Draw the shape
  ellipse(xpos, ypos, 40, 40);
  
}

void serialEvent(Serial myPort) {
  // read the serial buffer:
  String myString = myPort.readStringUntil('\n');
  if (myString != null) {
    myString = trim(myString); // remove whitespace chars (e.g. '\n')
    // split the string at the commas
    // and convert the sections into integers
    int sensors[] = int(split(myString, ','));
    // now print out those three integers using a for() loop, like so
    for(int i=0; i<sensors.length; i++) {
      print("Sensor " + i + ": " + sensors[i] + "\t");
    }
    // add a linefeed at the end
    println();
    
    // assign the sensor values to xpos & ypos
    if (sensors.length > 1) {
      xpos = sensors[0];
      ypos = sensors[1];
      // the button will send 0 or 1.
      // this converts them to 0 or 255 (black or white)
      fgcolor = sensors[2] * 255;
    }
  }
}

This code can also be found here.

The parsing happens in the serialEvent() method. The Serial.println() in Arduino attaches a newline and a carriage return. Newlines, carriage returns, spaces, and tabs are all considered whitespace. So we use the trim() method to remove it. We then split the string into three separate strings at the commas using the split() method, making sure to cast them as integers. We then assign the first two values (the analog sensors) to the x and y positions of the ellipse and the third value (the button state) will toggle the ellipse between black and white.

Handshaking

Punctuation helps keep your data in order, but because asynchronous serial communication is asynchronous, you can run into a problem when the sender sends faster than the receiver can read. When this happens, the receiver program slows down as the serial buffer fills up. You can manage this by implementing some form of flow control. The simplest way do to this is by using handshaking (aka call-and-response). With this method, the sending program only sends when it is requested by the receiving program, while the receiving program has to request new data every time it finishes reading/processing what it has.

We can convert the punctuation method above to a handshaking method fairly simply. First, we modify the Arduino code by adding a new method at the end of the sketch called establishContact(). This method continuously sends out a message on startup until it gets a byte of data from Processing. Then, we modify the loop() function by adding an if() statement to look for incoming serial data and read it. The rest of the Arduino sketch remains the same.

const int btnPin = 2; // digital input

void setup() {
  // start serial port at 9600 bps
  Serial.begin(9600);
  // configure the digital input
  pinMode(btnPin, INPUT);

  establishContact();  // send data establish contact until receiver responds
}

void loop() {
  // if we get a valid byte, read the sensors
  if (Serial.available() > 0) {
    // read the incoming byte
    int inByte = Serial.read();
  
    // read the analog sensor
    int sensorVal = analogRead(A0);
    // print the results
    Serial.print(sensorVal);
    Serial.print(",");
  
    // read the analog sensor
    sensorVal = analogRead(A1);
    // print the results
    Serial.print(sensorVal);
    Serial.print(",");
    
    // read the button state
    sensorVal = digitalRead(btnPin);
    // print the results:
    Serial.println(sensorVal);
  }
}

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("0,0,0");   // send an initial string
    delay(300);
  }
}

This code can also be found here.

When you run this code and open the Serial Monitor, you will see:

0,0,0
0,0,0
0,0,0
0,0,0

Next, we will modify the Processing sketch to look for this message and send a response. Here, we add a new global variable called contact and modify the serialEvent()method to look for the “0,0,0” message from the microcontroller. When it gets the initial message, it will send out a byte to request data. From then on, it will read in the string, parse it, and send a byte to request more data when it’s done.

/*
 * AnalogSensorsButton_Handshaking
 *
 * Carlos Castellanos
 * August 5, 2020
 *
 * Example of serial communication between Processing & Arduino using the
 * "call-and-response" (handshaking) method
 * Arduino sends the data for three sensors as ASCII and Processing
 * uses that data not to control the position and color of a shape on the screen.
 * 
 *
 */


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

Serial myPort;              // The serial port

float fgcolor = 0;          // Fill color defaults to black
float xpos, ypos;           // Starting position of the ball

boolean contact = false;    // Whether you've heard from the microcontroller
 
void setup() {
  size(800, 600);
  
  // List all the available serial ports
  println(Serial.list());
  
  // Change the number in the Serial.list() array to the appropriate number
  // of the serial port that your microcontroller is attached to.
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 9600);
  
  // don't generate a serialEvent() until you get an ASCII newline character
  myPort.bufferUntil('\n');
}

void draw() {
  background(#2b9468); // green background
  fill(fgcolor);
  
  // Draw the shape
  ellipse(xpos, ypos, 40, 40);
  
}

void serialEvent(Serial myPort) {
  // read the serial buffer:
  String myString = myPort.readStringUntil('\n');
  if (myString != null) {
    myString = trim(myString); // remove whitespace chars (e.g. '\n')
    
    // wait & listen until you hear from the microncontroller
    if(contact==false) {
      if(myString.equals("0,0,0")) {
        myPort.clear(); // clear the serial buffer
        contact = true;
        myPort.write(65); // send back a byte (doesn't matter what) to ask for more data
      }
    } else {  // if you have heard from the microcontroller, proceed
      // split the string at the commas
      // and convert the sections into integers
      int sensors[] = int(split(myString, ','));
      // now print out those three integers using a for() loop, like so
      for(int i=0; i<sensors.length; i++) {
        print("Sensor " + i + ": " + sensors[i] + "\t");
      }
      // add a linefeed at the end
      println();
      
      // assign the sensor values to xpos & ypos
      if (sensors.length > 1) {
        xpos = sensors[0];
        ypos = sensors[1];
        // the button will send 0 or 1.
        // this converts them to 0 or 255 (black or white)
        fgcolor = sensors[2] * 255;
      }
    }
    // when you've parsed the data you have, ask for more
    myPort.write(65);
  }
}

This code can also be found here.

Be Creative

The device you just created contains the same basic functionality of a mouse: two analog sensors that affect X/Y position, and a button. Can you create another physical interface with similar functionality but different interface? A video editor that scrubs forward and back when you tilt a wand? Breathing to generate images and/or text? A flashlight-based musical player? Try to brainstorm a bunch of these, then create a prototype in Arduino and Processing, or whatever programming environment you choose. Come up with a physical interface that clearly maps physical actions to computational media. Figure out which actions can and should be possible at the same time. This is not a requirement of this lab but a suggestion that you can use for your projects.