All posts by luobinwang





This is the final project for my pComp I class. The initial idea was to create a light illusion inside of a gate, however in the proposal the gate was only around 2 feet high or lower. After the review in class, I decide to make this an art installation in my locker and make it interactive. The final concept was two use several boards(not limited in 2), and place light strip on all edges. By manipulating the lightness of each edges, the light should create an illusion that the space is shifting with the movement of the audience. During the making process the broads were being limited in 2, but still delivers decent effect. I used two meter NeoPixel from Adafruit, which comes with a library only for Arduino, which left me no choice but not using firmata. The biggest problem when doing this project is communication. Sending values from Arduino to openFrameworks is easy, but the protocol doesn’t support the other way that well(or I haven’t found the best way). It took me a long time to make the Arduino board to no only separate the serial value but also find the head of the string. The face detection was done in with ofxfacetracker. To have the best result, the tracking point was set to the center of nose.


_________arduino code_________

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// released under the GPLv3 license to match the rest of the AdaFruit NeoPixel library
#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
#define PINLEFTBIG 13
#define PINRIGHTBIG 12
#define PINUPBIG 10
#define PINDOWNBIG 11

#define PINLEFTS 9
#define PINRIGHTS 8
#define PINUPS 6
#define PINDOWNS 7

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS1 10
#define NUMPIXELS2 30

#define NUMPIXELS3 5
#define NUMPIXELS4 15
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter–see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixelleft = Adafruit_NeoPixel(NUMPIXELS2, PINLEFTBIG, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixelright = Adafruit_NeoPixel(NUMPIXELS2, PINRIGHTBIG, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixelup = Adafruit_NeoPixel(NUMPIXELS1, PINUPBIG, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixeldown = Adafruit_NeoPixel(NUMPIXELS1, PINDOWNBIG, NEO_GRB + NEO_KHZ800);

Adafruit_NeoPixel pixellefts = Adafruit_NeoPixel(NUMPIXELS4, PINLEFTS, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixelrights = Adafruit_NeoPixel(NUMPIXELS4, PINRIGHTS, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixelups = Adafruit_NeoPixel(NUMPIXELS3, PINUPS, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixeldowns = Adafruit_NeoPixel(NUMPIXELS3, PINDOWNS, NEO_GRB + NEO_KHZ800);
int delayval = 0; // delay for half a second

int disX=0;
int disY=0;

int noseX = 0;
int noseY = 0;
int valueX, valueY;

void setup() {

pixelleft.begin(); // This initializes the NeoPixel library.

pixellefts.begin(); // This initializes the NeoPixel library.


void loop() {
// noseX = getNoseX();
// noseY = getNoseY();
for(int i=0;i<10;i++){
if (Serial.find(“s”)){
noseX = Serial.parseInt();
noseY = Serial.parseInt();
if(noseX != NULL){
if(noseX >= 70 || noseX <= 500 ){
valueX = noseX ;

Serial.print (valueX);

if(noseX != NULL){
if(noseY >= 70 || noseY <= 400 ){
valueY = noseY ;

disX= map(valueX, 60, 520, 0, 230);
disY= map(valueY, 80, 400, 0 ,255);
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixelup.setPixelColor(i, pixelup.Color(255-disY,255-disY,255-disY)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
pixeldown.setPixelColor(i, pixeldown.Color(disY,disY,disY)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
// Delay for a period of time (in milliseconds).

for(int i=0;i<30;i++){
disX= map(valueX, 60, 520, 0, 230);
disY= map(valueY, 80, 400, 0 ,255);

// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixelleft.setPixelColor(i, pixelleft.Color(disX,disX,disX)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
pixelright.setPixelColor(i, pixelright.Color(230-disX,230-disX,230-disX)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.

delay(delayval); // Delay for a period of time (in milliseconds).

for(int i =0;i<5;i++){

disX= map(valueX, 60, 520, 0, 230);
disY= map(valueY, 80, 400, 0 ,255);
pixelups.setPixelColor(i, pixelups.Color(255-disY,255-disY,255-disY)); // Moderately bright green color.;
pixeldowns.setPixelColor(i, pixeldowns.Color(disY,disY,disY)); // Moderately bright green color.;

for(int i =0;i<15;i++){

disX= map(valueX, 60, 520, 0, 230);
disY= map(valueY, 80, 400, 0 ,255);
pixellefts.setPixelColor(i, pixellefts.Color(disX,disX,disX)); // Moderately bright green color.; // This sends the updated pixel color to the hardware.
pixelrights.setPixelColor(i, pixelrights.Color(230-disX,230-disX,230-disX)); // Moderately bright green color.;




_______openFramworks code___________


#include “testApp.h”

using namespace ofxCv;

using namespace cv;

void testApp::setup() {







    vector <ofSerialDeviceInfo> deviceList = serial.getDeviceList();



    // this should be set to whatever com port your serial device is connected to.

    // (ie, COM4 on a pc, /dev/tty…. on linux, /dev/tty… on a mac)

    // arduino users check in arduino app….

    int baud = 9600;

    serial.setup(0, baud); //open the first device

    //serial.setup(“COM4”, baud); // windows example

    //serial.setup(“/dev/tty.usbserial-A4001JEC”, baud); // mac osx example

    //serial.setup(“/dev/ttyUSB0”, baud); //linux example






cam.initGrabber(640, 480);










void testApp::update() {


    bSendSerialMessage = true;


    noseBase = tracker.getImageFeature(ofxFaceTracker::NOSE_BASE).getCentroid2D();


    int valueX = noseBase.x;

    int valueY = noseBase.y;


    noseX = ofToString(valueX);

    noseY = ofToString(valueY);





    //update nose position

   // ofLog(OF_LOG_NOTICE) << “nosePosition XY  ” << noseBase.x , noseBase.y;



    //camera function


if(cam.isFrameNew()) {

if(tracker.update(toCv(cam))) {








    //serial write


    if (bSendSerialMessage){


        // (1) write the letter “a” to serial:

        string a = “s”; // string a









        //string b = “s5,200\n”;

        //unsigned char* nextChar = (unsigned char*) b.c_str();




        unsigned char* valChar = (unsigned char*) a.c_str(); // cast from string to unsigned char*

       // ofLog(OF_LOG_NOTICE) << “warning  ” << valChar;


        //ofDrawBitmapString(a, 30, 100); // draws a

        //ofDrawBitmapString(string((char *)b), 30, 130); // draws b


        //unsigned char buf[3] = {‘2’, ‘,’, ‘1’};

        //serial.writeBytes(&buf[0], 3);


        serial.writeBytes(&valChar[0], 8);

        //serial.writeBytes(&nextChar[0], 6);





        // (2) read

        // now we try to read 3 bytes

        // since we might not get them all the time 3 – but sometimes 0, 6, or something else,

        // we will try to read three bytes, as much as we can

        // otherwise, we may have a “lag” if we don’t read fast enough

        // or just read three every time. now, we will be sure to

        // read as much as we can in groups of three…


        nTimesRead = 0;

        nBytesRead = 0;

        int nRead  = 0;  // a temp variable to keep count per read


        unsigned char bytesReturned[3];


        memset(bytesReadString, 0, 4);

        memset(bytesReturned, 0, 3);


        while( (nRead = serial.readBytes( bytesReturned, 3)) > 0){


            nBytesRead = nRead;



        memcpy(bytesReadString, bytesReturned, 3);


        bSendSerialMessage = false;

        readTime = ofGetElapsedTimef();



        ofLog(OF_LOG_NOTICE) << “return  << bytesReadString;






void testApp::draw() {


cam.draw(0, 0);


int w = 100, h = 12;



ofTranslate(5, 10);









Final Proposal

 A magic door. 



The idea comes from Doraemon, a Japanese anime. This Random Door will have the ability to lead the user to anywhere they want.

The LCD will create a fake 3D space inside the open door, generating a virtual space.  The space will respond to human interaction by tracking their faces. This will affect the perspective and movement of the inside world. There is also a accelerometer to make it respond to the movement by itself.



The simulated behavior of the 3D space will look familiar to this:


Glove Instrument with Arduino and SuperCollider 

An music instrument using 4 flex sensors and an Arduino board. The readings from flex sensors are being sent to SuperCollider at real time. Firmata support is outdated on SuperCollider, so I’m using serial port instead. The signals strings are first sent to SC, separated by commas, then split function is used to separate them into array, the values in the array are assigned to variables one by one.

The data returned from flex sensor are values that is altered by its resistance. Some of the sensor was not that sensitive anymore so every mapping value should be adjusted manually.

4 buses are used in SC, 3 of them based on SinOsc(sine wave), one is based on RLPF(low pass filter). Codes in Arduino are simply reading and mapping out the value for SC to use.

Note that SC is a real time audio synthesis program, it could only possible to generate sound be initiated by starting the server and run the coding, which cannot be done automatically. It could be fun if SC behave like MaxMSP that is much more user friendly.

All codes are on Github:


Arduino code____________________________________________________________

void setup()

void loop()
int sensor, sensor2, sensor3, sensor4, degrees, degrees2, degrees3, degrees4;
sensor = analogRead(0);
sensor2 = analogRead(1);
sensor3 = analogRead(2);
sensor4 = analogRead(3);

degrees = map(sensor, 768, 853, 400, 500);
degrees2 = map(sensor2, 768, 853, 380, 500);
degrees3 = map(sensor3, 790, 853, 0.001, 20);
degrees4 = map(sensor4, 768, 853, 400, 800);

// Serial.print(“[“);




SuperCollider code _____________________________________________

arg freq = 300,freq2 = 400, freq3 =20, freq4 = 400 ;
var out;
var out2;
var out3;
var out4;
var freqMap;
out =[freq,freq4], 0,0.3);
out2 =, 0,0.3);
out3 = {[25,35].midicps, 0.15),, 0, 10, 72).midicps, 0.1, 0.1)};
out4 =[1,0.40],[0,0.6],freq2*2,freq2*3).trunc([200,600])*[1,-1]

//courtesy to Lance Putnam
//””, out);, out2);, out3);, out4);

//a = Synth(“tones”, [“freq”,600,”freq2″,500,”freq3″,20, “freq4”,700]);


p = SerialPort(
baudrate: 9600,
crtscts: true);
a = Synth(“tones”, [“freq”,400,”freq2″,400,”freq3″,20, “freq4”,400]);
r= Routine({

var byte, str, res, tone1, tone2, tone3, tone4;{|i|
if(, {
str = “”;
while({byte =; byte !=13 }, {
str= str++byte.asAscii;
res= str.split($,).asInteger;
tone1 =;
tone2 =;
tone3 =;
tone4 =;

(“test value:”+res).postln;

a.set(“freq”,tone1,”freq2″, tone2,”freq3″,tone3, “freq4”,tone4);







The goal is to make a rain wiper for a glass. Whenever it detects rain, it starts wiping. This project uses water sensor, servo motor and an Arduino. Didn’t find any rubber to imitate the wiper on the windshield on a car, so instead I used a piece of transparent plastic. What to be improved is, the water doesn’t vanish because of the plastic is too loose to touch the glasses surface. This will be improved in next phase.

I saw this after doing the project..:


//by peterobbin

//courtesy to:
// Sweep
// by BARRAGAN <>
// This example code is in the public domain.

#include <Servo.h>

Servo wiper;
int waterPin = 1;
int waterValue = 0;
int servoPin = 2;
int pos = 90;

void setup()

void loop(){
waterValue = analogRead(waterPin);
//float servoW = map(waterValue, 0, 1024, 0, 179);
if (waterValue > 400 && waterValue < 650){
else if(waterValue >= 650){

void swipeSlow(){

for(pos = 90; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
wiper.write(pos); // tell servo to go to position in variable ‘pos’
delay(10); // waits 15ms for the servo to reach the position

for(pos = 0; pos < 90; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
wiper.write(pos); // tell servo to go to position in variable ‘pos’
delay(10); // waits 15ms for the servo to reach the position

void swipeFast(){

for(pos = 90; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
wiper.write(pos); // tell servo to go to position in variable ‘pos’
delay(5); // waits 15ms for the servo to reach the position
for(pos = 0; pos < 90; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
wiper.write(pos); // tell servo to go to position in variable ‘pos’
delay(5); // waits 15ms for the servo to reach the position



This device is using Arduino, a photocell, an accelerometer, and a LCD. The goal is to help people to take more stable shots of photo/video by notification. In a darker environment, the helper will tell user to hold steady until it’s stable enough to take a clear photo. The helper will automatically turns off when there’s enough light.

The photocell senses the lightness of the environment. A line code is checking the value, if the value is low enough, it will check the realtime reading from accelerometer and give out suggestions.

Test without enclosure…………………………………






* peterobbin
* Courtesy To:
* SFE_MMA8452Q Library Basic Example Sketch
* Jim Lindblom @ SparkFun Electronics
* Original Creation Date: June 3, 2014
* This sketch uses the SFE_MMA8452Q library to initialize the
* accelerometer, and stream values from it.
#include <Wire.h> // Must include Wire library for I2C
#include <SFE_MMA8452Q.h> // Includes the SFE_MMA8452Q library

// Begin using the library by creating an instance of the MMA8452Q
MMA8452Q accel;

float lastasValue;
float asValue;
float asThreshold = 0.015;
float lightThreshold = 220;
int photocellPin = A0;
int pcValue;
// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// The setup function simply starts serial and initializes the
// accelerometer.
void setup()
Serial.println(“MMA8452Q Test Code!”);


// set up the LCD’s number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
//lcd.print(“HOLD STEADY”);
void loop()

pcValue = analogRead(photocellPin);

// Use the accel.available() function to wait for new data
// from the accelerometer.
if (accel.available())
// First, use to read the new variables:;

// will update two sets of variables.
// * int’s x, y, and z will store the signed 12-bit values
// read out of the accelerometer.
// * floats cx, cy, and cz will store the calculated
// acceleration from those 12-bit values. These variables
// are in units of g’s.
if (pcValue < lightThreshold){
lcd.setCursor(13, 1);
lcd.print(” ON”);
lcd.setCursor(0, 0);
lcd.print(“STABILIZE HELPER”);
lcd.setCursor(13, 1);


void printCalculatedAccels()
lastasValue = asValue;
asValue =;
float a = asValue – lastasValue;
float compareValue = abs(a);
Serial.print(compareValue, 3);
Serial.print(, 3);
Serial.print(, 3);
Serial.print(, 3);
if(compareValue > asThreshold){
lcd.setCursor(0, 0);
lcd.print(“HOLD STEADY “);
lcd.setCursor(0, 0);
lcd.print(“OKAY TO SHOOT “);

lcd.setCursor(0, 1);




is is the Fritzing sketch for my first project. I don’t know if I’m doing right because, in order to make a better looking schematic map, I have to check it every time I insert something to the breadboard. Otherwise the map will be messed up…











A simple Arduino – Processing communication project.

The 3 LEDs are controlled by a button switch. The potentiometer is used to control one of the green lights, and send serial data to processing. Processing simply uses the data to draw a dynamic sphere on the screen.







int switchState = 0;

void setup(){
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(2, INPUT);
pinMode(A1, INPUT);
void loop(){

switchState = digitalRead(2);
float wait = map(analogRead(A1), 0, 1024, 0, 100);

if(switchState == LOW){

digitalWrite(3, HIGH);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, LOW);
digitalWrite(3, LOW);
digitalWrite(4, LOW);
digitalWrite(5, HIGH);

digitalWrite(4, HIGH);
digitalWrite(5, LOW);





import processing.serial.*;
Serial myPort;
String val;
float rad;
void setup()
size(500, 500, P2D);

// serial example from processing
// I know that the first port in the serial list on my mac
// is Serial.list()[0].
// On Windows machines, this generally opens COM1.
// Open whatever port is the one you’re using.
String portName = Serial.list()[1]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this,”COM3″, 9600);
void draw()
fill(0, rad, 0);
if ( myPort.available() > 0)
{ // If data is available,

//try read port data, failed
// float inByte =;
val = myPort.readStringUntil(‘\n’);

//exclude null for result
//if not, will return null pointer exception
if(val != null){
rad = float(val);

map(rad, 0, 1024, 0, 255);
ellipse(rad, rad, rad, rad);

//print it out in the console

// rad = float(val);




Hi everybody!

My name is Luobin,  from Beijing, China. I’m a native Beijinger. If you have a plan traveling to Beijing, just ask me about everything!

I have a background in graphic design, UI/UX design. Before Parsons, I did some works related to service design, motion graphics , user-interface and video-editing. Although some of them involved hardware stuff,  it didn’t really give me a chance to get to know about pComp  and do it my self.

After all these years designing only for visuals and ideas, it’s time to explore how I can push it harder to archive something all by myself.  It would be interesting to see transferring and recreating the interaction experience from virtual to physical –  the various type of  physical feedback to it. And I would also like to try experimenting with sound, it will be sooooo much fun!!!!!