User login

Powered by Drupal, an open source content management system

Theory and Practice of Tangible User Interfaces

Dueling Piezos

Submitted by Simon Tan on Wed, 11/12/2008 - 21:29

Assignment: Synthesis: Music Instrument (group work)

Collaborators:

Collaborators: jonyen, Michael Manoochehri, michael_lee

Note: For a more readable report, please see the off-site version here:

http://blogs.ischool.berkeley.edu/simontan/2008/11/13/dueling-piezos/

(However, the video is only available on this page.)

Description

Our goal in this lab was to create a "musical instrument" utilizing the input/output devices we learned about this semester. Synthesizing at least four input devices, we created a networked "instrument" that is played with two people situated at different (possibly remote) stations.

The input devices at each station are one piezo (a "tapping" motion is used as input) and one potentiometer. The potentiometer controls the desired tone of the remote piezo, and tapping the local piezo sends a signal to activate the remote piezo. Thus, by tapping the local piezo and adjusting the potentiometer, a person at one station can send his 'tone' to the person at the other station and vice-versa. (His 'tone' will also be played on his local output piezo, so both parties are always aware of their own actions.)

We imagine a 'matching' game (i.e. in the vein of the game of Simon) could be played where each person tries to match the tone of the other.

Components Used

  • 2 Arduino microcontroller circuits
  • 4 piezo buzzers (2 as input, 2 as output)
  • 2 potentiometers as input
  • Insulated copper wire
  • Various flavors of resistors to keep piezo noise low

Arduino Code

In an earlier attempt at providing for more interesting output options, we wrote code to trigger a servo's movement whenever a piezo was tapped. This added noise (by the servos) which had undesirable effects. We were hoping to use the servos to hit a bell or chimes, but were unable to find any materials to serve that purpose. Thus, we eliminated the servos from our system altogether, but kept the code here for reference.

 

/** Unused Servo Code ---
#include <Servo.h>

/** Output - Servo
#define SERVO_PIN 11 // Control pin for servo motor
#define MIN_ANGLE 0 // Minimum servo position
#define MAX_ANGLE 150 // Maximum servo position

// Define states for the servo state machine
#define PULSE_ON 0
#define WAIT_PULSE_ON 1
#define PULSE_OFF 2
#define WAIT_PULSE_OFF 3

Servo servo;
int state = 0; // For the state machine
long nextMillis = 0; // Timing value
int angle = 0; // Amount to angle the servo
int servCount = 0; // Servo trigger value

/** Output - Piezzo **/
#define SPEAK_PIN 9 // Control pin for piezzo speaker
int piezCount = 0; // Piezzo trigger value
int toneValue = 0; // Pot value from serial input

char chIn; // Character read in from serial port

/** Input - Piezzo **/
#define TAP_PIN 0
#define TAP_THRESHOLD 60
int inTapVal = 0;

/** Input - Potentiometer **/
#define POT_PIN 1
int inPotVal = 0;

void setup() {
// Set output pins
//pinMode(SERVO_PIN, OUTPUT);
pinMode(SPEAK_PIN, OUTPUT);
//servo.attach(SERVO_PIN); // Activate servo
//angle = MIN_ANGLE; // Set the motor position value to the minimum
Serial.begin(9600); // Start serial port
}

void loop() {
// If there's input waiting on the serial port, read all of it
if (Serial.available()) {
while ((chIn = Serial.read()) != -1) {
switch(chIn) {
case '0'...'9':
toneValue = toneValue * 10 + chIn - '0';
piezCount = 1; // Get the piezzo ready to buzz once
break;
case 's':
//servCount++;
break;
}
}
}

// Any input from the two input devices on the board?
inTapVal = analogRead(TAP_PIN); // Read value from Piezzo input
inPotVal = analogRead(POT_PIN); // Read value from pot

if (inTapVal >= TAP_THRESHOLD) { // If knock detected
//servCount++;
Serial.println(inPotVal); // Triggers the message over the network

// Also fire our local piezo
piezCount = 1;
toneValue = inPotVal;
}

/** Output to Piezo **/
digitalWrite(SPEAK_PIN, LOW); // Reset speaker to OFF

if (piezCount > 0 && toneValue > 200) {  
for(int i = 0; i < 100; i++ ) { // Turn on momentary sound (pot-based pitch sound)
digitalWrite(SPEAK_PIN, HIGH);
delayMicroseconds(toneValue/4);
digitalWrite(SPEAK_PIN, LOW);
delayMicroseconds(toneValue/4);
}

piezCount--;
toneValue = 0;
}

/** Unused Servo Code ---
// If the boolean is true, we go through one round of servo movement
// State machine to control which action to perform
if (servCount > 0) {
switch (state) {
case PULSE_ON:
angle = MAX_ANGLE; // send servo to max position
nextMillis = millis() + 300; // wait 300 ms
state = WAIT_PULSE_ON;
break;
case WAIT_PULSE_ON:
if (millis() > nextMillis) {
state = PULSE_OFF; // time is up
}
break;
case PULSE_OFF:
angle = MIN_ANGLE; // send servo to min position
nextMillis = millis() + 500; // wait half second between two rings
state = WAIT_PULSE_OFF;
break;
case WAIT_PULSE_OFF:
if (millis() > nextMillis) {
state = PULSE_ON; // time is up
servCount--; // one ring is done
}
break;
}
}

servo.write(angle);
Servo::refresh();
*/
}

Processing Code

The following is the network (socket programming) code that allows the proper messages to be sent between the two stations. The stations act as both client and server to each other (a peer-to-peer setup) and send messages whenever they receive input from their Arduino boards (i.e. through the local serial port).

import processing.serial.*;
import processing.net.*;

// Customize these values for the network/local ports
String COMPORT = "COM11"; // Local serial port where Arduino is
String REMOTE_IP = "136.152.144.62"; // Remote IP address of peer client/server
int NET_PORT = 5204; // Remote socket port of peer client we connect to / local port we run the server on

// Character constants
int CR = 13; // ASCII return == 13
int LF = 10; // ASCII linefeed == 10

String netBuf = "";

Serial arduinoPort; // This is the local Arduino board
Client remotePeer; // This is the peer we're talking to
Server myServer; // This is us

void setup() {
myServer = new Server(this, NET_PORT);
delay(5000); // Make sure servers on both sides are running first
arduinoPort = new Serial(this, COMPORT, 9600);
remotePeer = new Client(this, REMOTE_IP, NET_PORT);
}

/* Infinite loop - don't use */
void draw() {}

/* Called whenever data arrives over the network from our peer */
void clientEvent(Client peer) {
String sDataIn = peer.readString();
if (sDataIn == null) { return; }
println("Read string from client: " + sDataIn);
arduinoPort.write(sDataIn.trim()); // Push it to Arduino
}

/* Called whenever data arrives from the Arduino serial port */
void serialEvent(Serial p) {
String temp = arduinoPort.readString();
netBuf += temp;  

// Don't do anything until we read up to a newline
String[] m = match(netBuf, "\\n");
if (m == null) { return; }

println("Received from Arduino: " + netBuf);

// Hit up the clients connected to us
myServer.write(netBuf);
netBuf = "";
}

/* Called when a new client connects to our server */
void serverEvent(Server someServer, Client someClient) {
println("New client successfully connected: " + someClient.ip());
someServer.write("600"); // Confirm that connection works!
}

Images

Pictures of the dueling piezos setup:

Video