(edit sidebar)
Intro to Physical Computing Syllabus

Research & Learning

Other Class pages

Shop Admin

ITP Help Pages
Tom's pcomp site
DanO's pcomp site


Jeff Feddersen Fall 13

Syllabus Main? Labs & tutorials Books and Readings Parts & Supplies ClassPages

Info

What: Intro to Physical Computing Sections 3 and 4 ITPG-GT.2301.[3|4]
When: Wednesdays 9:05am - noon OR 3:05pm to 6pm
Where: NYU Tisch Building, 4th floor, Room 447
Who: Jeff Feddersen

Admin

(Only I should be able to use these links...)
AM class list
PM class list
Other faculty links

Contact

Please use my personal email: jfeddersen@gmail.com (I may not see my NYU email immediately).
Office hours: By appointment. I'm around for a residency on Thursdays, so finding time then will probably work.
Note: A few of the Residents are holding a Physical Computing Study Group on Fridays 11:00am-12:30pm in Meeting Room A/B. The format will be flexible, but it will be a combination of group review, walk-in office hours, and small group collaboration. We'll be honing the format as the semester presses on.

Grading

Participation & Attendance: 40%
Production Assignments: 40%
Journal: 20%

Supplies

See http://itp.nyu.edu/physcomp/Intro/Supplies

Useful Links

http://www.arduino.cc - Arduino website. Check out the Getting Started and the Reference pages
http://tigoe.net/pcomp/ - Tom Igoe's Physical Computing site
http://www.tigoe.net/pcomp/code/ - Tom's Code, Circuits & Construction blog
http://itp.nyu.edu/physcomp/sensors/ - ITP Sensor Workshop wiki from another class at ITP
http://itp.nyu.edu/mechanisms/ - Dustyn Roberts' class on mechanisms, very useful for when we do motors
http://learn.adafruit.com/ - Parts-supplier Adafruit has created a nice tutorial portal, especially for Raspberry Pi
https://learn.sparkfun.com/tutorials - So has SparkFun
http://kinasmith.com/itp-materials-and-resources-list - This *I think* was the resources list email mentioned in week 1 class. I copied to my website -kina
Class cam

Good References

Physical Computing: Sensing and Controlling the Physical World with Computers
Getting Started with Arduino
Make: Electronics
Getting Started in Electronics
Making Things Talk
Making Things Move
Practical Electronics For Inventors
Energy
There are no electrons; Electronics for earthlings
Fashioning Technology: A DIY Intro to Smart Crafting
http://whoworeitbetter.info

Class Blogs

Please press "Edit" at the bottom of this page, login and then enter the url to your blog below (if you are new to wiki formatting just follow the pattern of the examples below and substitute with your url and your name).

For example: Jeff Feddersen

Morning Class

Morning class final projects!

  1. Amelia Winger-Bearskin
  2. Adarsh Kosuru
  3. Aankit Patel
  4. Allison Ye
  5. Woonyung Choi
  6. Kina Smith
  7. Dan Melancon
  8. Rodrigo Narciso G. da Silva
  9. Federico Burch
  10. Nevena Kocic
  11. Zander Whitehurst
  12. Salem Almansoori
  13. Caitlin Weaver
  14. Clara Santamaria
  15. Michael Ricca
  16. Eunice Kim

Afternoon Class

Afternoon class final projects!

  1. Justin Restauri
  2. Michael Oneppo
  3. Peter Terezakis
  4. Monique Saunders
  5. Joy Li
  6. Leslie Lin
  7. Claire Kearney-Volpe
  8. Yu HU
  9. Isi Azu
  10. Neil Solomon
  11. Chrisanthy Surya
  12. Eozin Che
  13. Corey Boling
  14. Regina Hong
  15. Rodrigo Derteano

Class Schedule and Notes

Week 1 - September 4

Here are some alternate schematics for this week's transistor lab. I personally find this style more legible, but it leaves out some connections. For more on schematics, this is a great write up: http://www.beavisaudio.com/techpages/SchematicToReality/

Week 2 - September 11

Here's a video and some notes on putting together a "generic tactile interface":

Week 3 - September 18

Code from in class, using the GTI from the video. This should all look pretty familiar, with the possible exception of the arrays used for the digital inputs. Note - there are more comments than code in the example below, which is good practice.

/*
Generic Tactile Interface applied to game control for Class 3
3 potentiometers on A0, A1, A2
6 switches on D2-D7
2 LEDs on D8 and D9
*/


byte blueLED = 8, yellowLED = 9;
int flashDelay=10; //really loop delay

int lastA0 = 0, currentA0 = 0; //map mouse moves to difference in first pot

//with 6 digital inputs all working the same way, its easier to use arrays and for loops to handle them.
int buttonPins[] = {2, 3, 4, 5, 6, 7}; //these are the pins that have buttons attached
char keyMapping[] = {KEY_RIGHT_ARROW, KEY_UP_ARROW,
                     KEY_DOWN_ARROW, KEY_LEFT_ARROW,
                     KEY_LEFT_ALT, KEY_LEFT_SHIFT}; //These are the key commands associated with each button
int buttonStates[] = {0, 0, 0, 0, 0, 0}; //These store the current state of each button
int lastButtonStates[] = {0, 0, 0, 0, 0, 0};  //These store the previous state of each button

void setup() {
  //Set our outputs
  pinMode(blueLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);

  //Set our potentiometer inputs
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);

  //Set our digital inputs  
  for (int i=0; i<6; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP); //This is a clearer way to do the same thing as the next two lines
    //pinMode(buttonPins[i], INPUT);
    //digitalWrite(buttonPins[i], HIGH);    
  }

  //Start all communication to the laptop
  Serial.begin(9600);
  Mouse.begin();
  Keyboard.begin();
}

void loop() {

  //Send debugging output to the serial window
  Serial.println("#####0#######");
  Serial.print("A0: ");
  int currentA0 = analogRead(A0);
  //Serial.println(currentA0);
  //flashDelay = map(temp, 0, 1023, 25, 500);
  //flashDelay = temp;
  Mouse.move(0, currentA0 - lastA0, 0); //Mouse will only be moved by the Arduino when A0 changes value
  lastA0 = currentA0;

  //These are not mapped to any keyboard or mouse controls  
  Serial.print("A1: ");
  Serial.println(analogRead(A1));
  Serial.print("A2: ");
  Serial.println(analogRead(A2));

  Serial.println(); //Empty println() statements help the serial window formatting.
  //Each of the 6 digital pin will work the same way.
  //For each, read in its state and record it in the current state array
  //Report the current state via serial
  //Compare the state to the last state.
  //IF its changed, then...
  //IF its LOW (because pushing the button connects the pulled-up pin to ground)
  //THEN send the key press that corresponds to that button
  //ELSE, its HIGH, so the button has been released, so send the cooresponding key release command
  //Finally, record the current state in the last state array, for comparison next time through the loop
  for (int i=0; i<6; i++) {
    //Serial.print(i + ": ");
    buttonStates[i] = digitalRead(buttonPins[i]);
    Serial.println(buttonStates[i]);
    if (lastButtonStates[i] != buttonStates[i]) {
      if (buttonStates[i] == LOW) {
        Keyboard.press(keyMapping[i]);
      } else {
        Keyboard.release(keyMapping[i]);
      }
    }
    lastButtonStates[i] = buttonStates[i];
  }
  Serial.println();

  //Flash the LEDs, just 'cuz
  digitalWrite(blueLED, HIGH);
  digitalWrite(yellowLED, LOW);
  delay(flashDelay);
  digitalWrite(blueLED, LOW);
  digitalWrite(yellowLED, HIGH);  
  delay(flashDelay);
}

Week 4 - September 25

Everything today - the various flavors of analog output - involves the precise timing of a digital pin turning on and off. Our Arduinos are good at generating square waves at various frequencies and with various duty cycles (time on vs time off). We can use analogWrite - a 490Hz square wave with duty a cycle between 0 and 100%. We can use the Servo library - a 60Hz square wave with a short pulse the length of which encodes the angle we want the servo to take. Or we can use tone(), which uses a 50% duty cycle at any frequency we want. All of these are just convenient wrappers that elaborate on the simplest loop we could write ourselves:


void setup() {
  pinMode(11, OUTPUT);

}

void loop() {
  digitalWrite(11, HIGH);
  digitalWrite(11, LOW); 
}

Which on the scope, looks like this:

Click the larger image, and you can see that those two operations together clock in at about 82 kHz (noted at the lower right hand side of the scope display), or 82,000 times a second.

We can get faster if we directly flip bits of "PORTB", a special byte in memory where each bit corresponds to the state of one of the Arduino's digital outputs.

void setup() {
  PORTB = B00000000;  
}

void loop() {
  PORTB = ~PORTB; //flip the bits. ~ = bitwise NOT
}

This goes more than twice as fast, at about 175 kHz (it looks wider because the time scale is smaller):

There are many faster chips, and faster code (say, foregoing the Arduino IDE and using C or assembler), but the real danger is not in non-optimal code wasting the chip's time, but in wasting our (human) time with unnecessary or premature optimizations. Focus on the output.

Some links from class:
The music we listened to was: http://www.1bitsymphony.com/
The arduino-based synth: http://www.critterandguitari.com/
And in shield form (no longer active): http://shieldlist.org/critter-and-guitari/pocket-piano

Week 5 - October 2
Please note: The syllabus has been updated to outline the next few weeks in detail, and to allocate effort in and between classes towards a two-week midterm project that will be presented in week 8. There is a lot of text in the syllabus, so I thought it would be useful to have a brief outline here:

Between class 5-6:

  • Decide on the project they'll do, come up with a description/set of drawings
  • Revisit the code for their project, assuming it's one of the earlier labs, so they have the basics down.

In-class week 6: a) design and implement a cardboard version of their concept b) Create a play test plan (really just a series of questions about what works for the user) c) Execute plan with other students in the class

Between week 6-7:

  • blog assignment
  • Implement the cardboard version with actual sensors and LEDs/vibrating motors/speakers (whatever simple outputs are necessary based on play test)

In-class week 7:

  • Fabrication demo
  • in-class: determine what's needed to move from cardboard to a more permanent enclosure, do design files and/or refine construction

Between 7-8:

  • Fabricate enclosure
  • Do wireless labs

In-class week 8:

  • Present midterms
  • Wireless telephone game (and we intro induction here, in question time)

Also note - the Buxton reading is "Sketching USER Experiences" not "Interactive Experiences".

From the 'Bot example today - the basic idea for serial FROM Processing TO Arduino is pretty straightforward. Each character sent maps to an action on the bot:

The data back FROM the Arduino TO Processing is more complex, because I want to transmit two floating point values (and maybe more data down the road). So I 1) send data across in lines (text followed by a new line) 2) identify valid lines based on some formatting, and 3) parse those lines into numbers. It looks like this:

The full code for both is below (long and lot's of extra stuff we haven't covered in depth!).

ARDUINO

/*
Control a small robot via XBEE
 Hardware is:
 XBee Series 2 radio on TX/RX
 Sparkfun Motor Driver 1A Dual Channel TB6612FNG to 2 micro gear motors
 2 PWM channels for motor speed
 4 digital pins to select direction on 2 channels
 Honeywell HMC5883L 3-axis magnetometer on Sunkee GY-273 breakout board (5V)
 On 2Wire/i2c bus (A4 and A5)
 Using library from http://www.loveelectronics.co.uk/Tutorials/8/hmc5883l-tutorial-and-arduino-library
 Red and Yellow LEDs on 11 and 12
 Servo on ~9.
 */


// Reference the I2C Library
#include <Wire.h>
// Reference the HMC5883L Compass Library
#include <HMC5883L.h>
//Servo library
#include <Servo.h>

//LEDs
const byte redLED = 11, yellowLED = 12;

//Motor
const byte pwmA = 6, pwmB = 5;
const byte aIn1 = 3, aIn2 = 4, bIn1 = 8, bIn2 = 7;
const byte motorA = 0, motorB = 1;
const byte FWD = 0, BK = 1;

//Servo:
Servo s1;
int servoPos = 0, servoStep = 5, servoMax = 150;

//Compass
HMC5883L compass;
int error = 0;
float scale = 1.3; //can be .88, 1.3, 1.9, 2.5, 4.0, 4.7, 5.6, 8.1
int headingUpdateIntervalMS = 50; //will send new heading every 500 ms
unsigned long lastUpdate = 0;

//SETUP  
void setup() {
  Serial.begin(9600);

  pinMode(redLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);

  Serial.println("Initializing servo...");
  s1.attach(9);

  Serial.println("Starting the I2C interface.");
  Wire.begin(); // Start the I2C interface.

  Serial.println("Constructing new HMC5883L");
  compass = HMC5883L(); // Construct a new HMC5883 compass.

  Serial.print("Setting scale to +/- ");
  Serial.print(scale);
  Serial.println(" Ga");
  error = compass.SetScale(scale); // Set the scale of the compass.
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));

  Serial.println("Setting measurement mode to continous.");
  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));

  //indicate setup complete
  delay(1000);
  s1.write(servoMax);
  delay(1000);
  for (int i=0; i<10; i++) {
    digitalWrite(redLED, HIGH);
    delay(50);
    digitalWrite(redLED, LOW);
    delay(50);
  }
  s1.write(servoPos);
}

//LOOP
void loop() {
  if (millis() - lastUpdate > headingUpdateIntervalMS) {
    //update timer has elapsed - send new heading data:
    sendHeading();
    lastUpdate = millis();
  }

  //Look for commands from CPU:
  while (Serial.available() > 0) {
    //Serial.print("OK");
    char ser = Serial.read();

    switch(ser) {
    case 'Y':
      digitalWrite(yellowLED, HIGH);
      break;
    case 'y':
      digitalWrite(yellowLED, LOW);
      break;
    case 'R':
      digitalWrite(redLED, HIGH);
      break;
    case 'r':
      digitalWrite(redLED, LOW);
      break;
    case 'W':
      setMotor(motorA, FWD, 255);
      setMotor(motorB, FWD, 255);
      break;
    case 'w':
      setMotor(motorA, FWD, 0);
      setMotor(motorB, FWD, 0);
      break;
    case 'S':
      setMotor(motorA, BK, 255);
      setMotor(motorB, BK, 255);
      break;
    case 's':
      setMotor(motorA, BK, 0);
      setMotor(motorB, BK, 0);
      break;
    case 'A':
      setMotor(motorA, FWD, 255);
      setMotor(motorB, BK, 255);
      break;
    case 'a':
      setMotor(motorA, BK, 0);
      setMotor(motorB, BK, 0);
      break;
    case 'D':
      setMotor(motorA, BK, 255);
      setMotor(motorB, FWD, 255);
      break;
    case 'd':
      setMotor(motorA, BK, 0);
      setMotor(motorB, BK, 0);
      break;
    case 'H': //request for heading, not needed if bot is autoreporting
      sendHeading();
      break;
    case 'i':
    case'I': //servo advance
      servoPos -= servoStep;
      if (servoPos < 0) servoPos = 0;
      s1.write(servoPos);
      break;
    case 'k':
    case'K': //servo retreat
      servoPos += servoStep;
      if (servoPos > servoMax) servoPos = servoMax;
      s1.write(servoPos);
      break;
    }

  }
}

void sendHeading() {
  MagnetometerScaled scaled = compass.ReadScaledAxis();
  Serial.print("X");
  Serial.print(scaled.XAxis);
  Serial.print("Y");
  Serial.print(scaled.YAxis);
  Serial.println();
}

void setMotor(byte motorID, byte dir, byte spd) {
  if (motorID == motorA) {
    if (dir == FWD) {
      digitalWrite(aIn1, LOW);
      digitalWrite(aIn2, HIGH);
    }
    else {
      digitalWrite(aIn1, HIGH);
      digitalWrite(aIn2, LOW);
    }
    analogWrite(pwmA, spd);
  }
  else {
    if (dir == FWD) {
      digitalWrite(bIn1, LOW);
      digitalWrite(bIn2, HIGH);
    }
    else {
      digitalWrite(bIn1, HIGH);
      digitalWrite(bIn2, LOW);
    }
    analogWrite(pwmB, spd);
  }
}

PROCESSING

import processing.serial.*;

Serial port;
int serialID = 0; //Change this based on the Serial port list
String serialBuffer;
boolean logToConsole = true;

float xHeading = 0, yHeading = 0;
PFont f;


void setup() {
  frameRate(60);
  size(800, 400);

  println(Serial.list());
  port = new Serial(this, Serial.list()[serialID], 9600);
  serialBuffer = "";

  f = createFont("Helvetica", 24);
  textFont(f, 24);
}

void draw() {
  background(32);

  fill(230);
  text("X: " + xHeading, 10, 50);
  text("Y: " + yHeading, width/2, 50);

  rect(10, height/2, 30, xHeading/2);
  rect(width/2+10, height/2, 30, yHeading/2);

  //also can use port.bufferUntil('\n');
  while (port.available () > 0) {
    char in = (char)port.read();
    if (in == '\n') {
      if (logToConsole) println();
      processSerial();
    }
    else {
      serialBuffer = serialBuffer + in;
      if (logToConsole) print(in);
    }
  }
}

void processSerial() {
  if (logToConsole) {
    println("Processing a line:");
    println(serialBuffer);
  }
  int x = serialBuffer.indexOf("X");
  int y = serialBuffer.indexOf("Y");
  if (x == 0 && y >= 0) {
    try {
      xHeading = Float.parseFloat(serialBuffer.substring(x+1, y));
      yHeading = Float.parseFloat(serialBuffer.substring(y+1, serialBuffer.length()));
    } catch(Exception e) {
      xHeading = -1; yHeading = -1;
    }

    if (logToConsole) {
      println("-> Line contains heading data:");
      println("\t X component is " + xHeading);  
      println("\t Y component is " + yHeading);
      println();
    }
  }
  serialBuffer = "";
}

void keyPressed() {
  if (key=='Y' || key=='y') {
    port.write('Y'); //command to turn yellow on
  }
  else if (key=='R' || key=='r') {
    port.write('R'); //command to turn Red on
  }
  else if (key=='W' || key=='w') {
    port.write('W'); //Go forward
  }
  else if (key=='S' || key=='s') {
    port.write('S'); //Go back
  }
  else if (key=='A' || key=='a') {
    port.write('A'); //
  }
  else if (key=='D' || key=='d') {
    port.write('D'); //
  }
  else if (key=='H' || key=='h') {
    port.write('H'); //
  }
  else if (key=='I' || key=='i') {
    port.write('i'); //
  }
  else if (key=='K' || key=='k') {
    port.write('k'); //
  }
}

void keyReleased() {
  if (key=='y' || key=='Y') {
    port.write('y'); //command to turn yellow off
  }
  else if (key=='R' || key=='r') {
    port.write('r'); //command to turn Red on
  }
  else if (key=='W' || key=='w') {
    port.write('w'); //
  }
  else if (key=='S' || key=='s') {
    port.write('s'); //
  }
  else if (key=='A' || key=='a') {
    port.write('a'); //
  }
  else if (key=='D' || key=='d') {
    port.write('d'); //
  }
}

Week 6 - October 9

October 16 - No class

Week 7 - October 23

Week 8 - October 30

Week 9 - November 6
The following are questions you should be able to answer without reference to outside material by the middle of the semester in Introduction to Physical Computing. Try to answer all of the questions below without resorting to copying external code or diagrams. Pseudocode is fine where you don't remember exact syntax. If you don't know where to start on a given question, consult with your instructor in office hours. Turn this in in week 10.

Self test: Self Test

Week 10 - November 13
Some links from class:
Tom Sachs Space Program at the Armory
Tim Hawkinson's Uber Organ
Pe Lang - Motors
Zimuone - more motors
Cute Theremins
Moog Fest 2014, with Circuit Bending contest
Events happen at Eyebeam
Gecko drives for bigger steppers
Sparkfun BigEasy stepper driver One of several they carry. Another one This one receives commands over SPI so your mcu doesn't have to block or count steps.
http://www.ultramotion.com/products/bug.php super precise, fast and strong (and expensive) piston actuator by ultramotion.
Week 11 - November 20
November 27 - No class
Week 12 - December 4\\

  Edit | View | History | Print | Recent Changes | Search Page last modified on January 10, 2014, at 03:51 PM