# Blog 14 Recitation 8: Serial Communication

Exercise 1: Make a Processing Etch-A-Sketch

1. Build a circuit with two potentiometers and write an Arduino sketch that reads their values and sends them serially. Then, write a Processing sketch that draws a circle and reads those two analog values from Arduino. modify the circle’s x and y values based on the input from Arduino. 

This is rather easy, so I didn’t meet much problem.

Arduino:

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

void loop() {
  int sensor1 = analogRead(A0);
  int sensor2 = analogRead(A1);
  Serial.print(sensor1);
  Serial.print(",");  // put comma between sensor values
  Serial.print(sensor2);
  Serial.println();
  delay(100);
}

Processing:

import processing.serial.*;
float x;
float y;

int NUM_OF_VALUES_FROM_ARDUINO = 2;   /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int sensorValues[];      /** this array stores values from Arduino **/

String myString = null;
Serial myPort;

void setup() {
  size(500, 500);

  setupSerial();
}

void draw() {
  background(0);
  getSerialData();
  printArray(sensorValues);
  x = (sensorValues[0]);
  y = (sensorValues[1]);
  circle(x, y, 20);
}

void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[2], 9600);

  myPort.clear();
  myString = myPort.readStringUntil( 10 );  // 10 = '\n'  Linefeed in ASCII
  myString = null;

  sensorValues = new int[NUM_OF_VALUES_FROM_ARDUINO];
}

void getSerialData() {
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = '\n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
        for (int i=0; i<serialInArray.length; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

2. Modify the code to use a line instead (like a real Etch-A-Sketch).  To do this, you will need to keep track of the previous x and y values so that you can draw a line from there to the new x and y positions. 

The problem is I need to store the x and y position of the previous state in order to draw a line, so I added a second array. And because the domain of potentiometer is from 0 to 1023, but the size of processing is only 500*5oo, it’s very hard to control the trait. So, I added a map function to modify them.

The interaction is that people input a direction of position with an action of their fingers and and computer output with animation accordingly.

Processing:

import processing.serial.*;
float x;
float y;

int NUM_OF_VALUES_FROM_ARDUINO = 2;   /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int sensorValues[];      /** this array stores values from Arduino **/
int sensorp[];
String myString = null;
Serial myPort;

void setup() {
  size(500, 500);
  setupSerial();
  sensorp = new int[NUM_OF_VALUES_FROM_ARDUINO];
  background(19,90,81);
}

void draw() {
  getSerialData();
  printArray(sensorValues);  
  x = map(sensorValues[0], 0, 1023, 0, 500);
  y = map(sensorValues[1], 0, 1023, 0, 500);
  stroke(255);
  line(sensorp[0], sensorp[1],x, y);
  sensorp[0]=int(x);
  sensorp[1]=int(y);
}

void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[2], 9600);
  
  myPort.clear();
  myString = myPort.readStringUntil( 10 );  // 10 = '\n'  Linefeed in ASCII
  myString = null;

  sensorValues = new int[NUM_OF_VALUES_FROM_ARDUINO];
  
}
void getSerialData() {
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = '\n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
        for (int i=0; i<serialInArray.length; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

Exercise 2:

Send values from Processing to Arduino based on the position of the bouncing ball. when the ball hits an edge of the screen. Then, make a circuit with your Arduino and a servo motor. 

The problem I met is that the delay is too short for the servo to rotate 90 degrees. So I have to add a boolean for prevState and currentState and add a delay in the if statement. 

The interaction is not that strong between the audience and the project. But it does create some fun.  Like in the video below, I made my favorite sticker, which is a chubby duck, and let its butt hit the ball to make it cuter.

 

Arduino:

#define NUM_OF_VALUES_FROM_PROCESSING 1    /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
#include <Servo.h>
/** DO NOT REMOVE THESE **/
int tempValue = 0;
int valueIndex = 0;
bool prevState = false;
bool currentState = false;
Servo myservo;
int processing_values[NUM_OF_VALUES_FROM_PROCESSING];
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  myservo.attach(11);
}
void loop() {
  getSerialData();
  currentState = processing_values[0];
 
  if (currentState != prevState && currentState == 1) {
    myservo.write(90);
    delay(100);
    myservo.write(0);
  } else {
    myservo.write(0);
  }
  prevState = currentState;
}
//receive serial data from Processing
void getSerialData() {
  while (Serial.available()) {
    char c = Serial.read();
    switch (c) {
      case '0'...'9':
        tempValue = tempValue * 10 + c - '0';
        break;
      case ',':
        processing_values[valueIndex] = tempValue;
        tempValue = 0;
        valueIndex++;
        break;
      case '\n':
        processing_values[valueIndex] = tempValue;
        tempValue = 0;
        valueIndex = 0;
        break;
    }
  }
}

Processing: 

import processing.serial.*;
int NUM_OF_VALUES_FROM_PROCESSING = 1;  /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int processing_values[] = new int[NUM_OF_VALUES_FROM_PROCESSING]; /** this array stores values you might want to send to Arduino **/
Serial myPort;
String myString;
int x=40;
int speedX = 7;
void setup() {
  fullScreen();
  background(0);
  setupSerial();
}
void draw() {
  background(0);
  circle (x, 200, 80);
  x= x+speedX;
  if (x > width-40 || x < 0+40) {
    speedX = -speedX;
  }
  if (x > width-40) {
    processing_values[0] = 1;
  } else {
    processing_values[0] = 0;
  }
  sendSerialData();
}
void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[2], 9600);
  myPort.clear();
  myString = myPort.readStringUntil( 10 );  // 10 = '\n'  Linefeed in ASCII
  myString = null;
}
void sendSerialData() {
  String data = "";
  for (int i=0; i<processing_values.length; i++) {
    data += processing_values[i];
    //if i is less than the index number of the last element in the values array
    if (i < processing_values.length-1) {
      data += ","; // add splitter character "," between each values element
    }
    else {
      data += "\n"; // add the end of data character linefeed "\n"
    }
  }
  //write to Arduino
  myPort.write(data);
  print(data); // this prints to the console the values going to arduino
}

Homework:

Use two pushbuttons connected to Arduino to control the display of two stars in Processing.

The problem I met is that I need to keep the star showing after I press the button once, so I created a count function for the HIGH state of button and use odd and even count to classify the situation.

The interaction here is that you can control the appearance and disappearance of a graph. 

Arduino:

int button1 = 2;
int button2 = 4;

int count1;
int count2;

void setup() {
  Serial.begin(9600);
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
}

void loop() {

if (digitalRead(button1) == HIGH){
  delay(100);
  count1 = count1 + 1;
} 
if (digitalRead(button2) == HIGH){
  delay(100);
  count2 = count2 + 1;
}
  Serial.print(count1);
  Serial.print(","); 
  Serial.print(count2);   
  Serial.println(); // add linefeed after sending the last sensor value

  // too fast communication might cause some latency in Processing
  // this delay resolves the issue.
  delay(100);

  // end of example sending values
}

Processing:

import processing.serial.*;


int NUM_OF_VALUES_FROM_ARDUINO = 2;   /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int sensorValues[];      /** this array stores values from Arduino **/

String myString = null;
Serial myPort;

void setup() {
  size(500, 500);
  setupSerial();
}

void draw() {
  background(0);
  getSerialData();
  printArray(sensorValues);
  if (sensorValues[0] % 2 == 1) {
    pushMatrix();
    translate(width*0.3, height*0.3);
    rotate(frameCount / 200.0);
    star(0, 0, 30, 70, 5);
    popMatrix();
  }
  if (sensorValues[1] % 2 == 1) {
    pushMatrix();
    translate(width*0.7, height*0.7);
    rotate(frameCount / 200.0);
    star(0, 0, 80, 100, 40);
    popMatrix();
  }
}

void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[ 2 ], 9600);

  myPort.clear();
  myString = myPort.readStringUntil( 10 );  // 10 = '\n'  Linefeed in ASCII
  myString = null;

  sensorValues = new int[NUM_OF_VALUES_FROM_ARDUINO];
}

void getSerialData() {
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = '\n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
        for (int i=0; i<serialInArray.length; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

void star(float x, float y, float radius1, float radius2, int npoints) {
  float angle = TWO_PI / npoints;
  float halfAngle = angle/2.0;
  beginShape();
  for (float a = 0; a < TWO_PI; a += angle) {
    float sx = x + cos(a) * radius2;
    float sy = y + sin(a) * radius2;
    vertex(sx, sy);
    sx = x + cos(a+halfAngle) * radius1;
    sy = y + sin(a+halfAngle) * radius1;
    vertex(sx, sy);
  }
  endShape(CLOSE);
}

Image Usage in Processing

1. Manipulate them to generate creative outputs: I used processing.org as reference and found the image() function.

Processing currently works with GIF, JPEG, and PNG images. The imageMode() function can be used to change the way these parameters draw the image. The color of an image may be modified with the tint() function. This function will maintain transparency for GIF and PNG images.

 img = loadImage("Toyokawa.jpg")

Parameters

  • img(PImage)the image to display
  • a(float)x-coordinate of the image by default
  • b(float)y-coordinate of the image by default
  • c(float)width to display the image by default
  • d(float)height to display the image by default

The PImage class contains fields for the width and height of the image, as well as an array called pixels[] that contains the values for every pixel in the image. To create a new image, use the createImage() function. Do not use the syntax new PImage().

Methods

  • loadPixels()Loads the pixel data for the image into its pixels[] array
  • updatePixels()Updates the image with the data in its pixels[] array
  • resize()Resize the image to a new width and height
  • get()Reads the color of any pixel or grabs a rectangle of pixels
  • set()Writes a color to any pixel or writes an image into another
  • mask()Masks part of an image with another image as an alpha channel
  • filter()Converts the image to grayscale or black and white
  • copy()Copies the entire image
  • blendColor()Blends two color values together based on the blending mode given as the MODE parameter
  • blend()Copies a pixel or rectangle of pixels using different blending modes
  • save()Saves the image to a TIFF, TARGA, PNG, or JPEG file

2. Use them as inputs by webcam: I found a tutorial video on YouTube as reference. And there’re following steps we can do to use a webcam:

-Install the Processing Video library, accessible under Sketch > Import Library > Add Library. Search for Video in the search box, and install the library from The Processing Foundation.

-Importing the library, and creating a setup function. If the camera has data available you read the data from it. This data is then displayed as an image, at the position 0, 0, which is the top left of the window.

import processing.video.*;
Capture cam;
void setup(){
 size(640,480);
 cam = new Capture(this, 640, 480);
 cam.start();
}
void draw(){
 if (cam.available()){
 cam.read();
 }
 image(cam,0,0); 
}

-We can also flip it, make it cycle using scale, add more effects using tint, and make it follow the mouse.

I can use it to make posters automatically with different people in front of the camera.

April 17, 2022, Jinqiao, Younian Liu

Leave a Reply

Your email address will not be published. Required fields are marked *