The Boundless Light-Leon Chen-Professor Gottfried Haider

Concept and design:

My project derived from the Infinite Mirror Room in the “teamLab Borderless Shanghai”. The room in the museum has countless light where each of them can link to another light and make a unique route. The mirror in the room reflects the light and makes the room a dream-like space. I also want to provide my users a fantastic experience through light. Besides, I read the article “Art, Interaction and Engagement” by Ernest Edmonds, in which he concluded a way of interaction is “relating” that provokes long-term interest in the users (12). I think the best way to achieve this is to make my project more artistic, just as the Infinite Mirror Room does. But I’m not going to build a room, instead, I decided to put neopixels in a box to achieve the effect. The purpose of my project is to allow users to click the mouse on Processing to make neopixels light up, which may give them an unforgettable experience. The light of neopixels appears as if it was boundless. During the user testing, I recieved suggestions on the exposion of neopixels in the box and the user interface problems. Therefore, I used some rice paper to cover up the neopixels, on the other hand, these paper are transparent so that light can come through. I also made more areas on the Processing for the user to click and expanded the interface area to the whole screen. I was also suggested to integrate the screen into the box, but since I’ve finished fabrication, it would be time-consuming to do this. Professor Gottfried adviced me to allow user to choose what color they want to distribute. However, this function would require an array in an array, which was beyond my abillity.

Fabricationa and Production:

Before

As for the fabrication, I originally decided to build the project according to the sketch above. I soon realized that neopixels were too long to be hung like that way, otherwise the box would be like a tall building, which was stupid. Therefore, I changed my plan and draw another sketch. But why did I choose neopixels? On one hand, it is long enough to exhibit the change of light colors. On the other hand, neopixels will make the difficulty of my project moderate. I once thought of using multiple LEDs to do the task, but they were small and thin, which were not something I wanted for my final project.

After

The wiring was rather simple, because I didn’t choose any sensors. On the contrary, the most difficult part was the coding. I initially used multiple states to control the neopixels with the number sent by the Processing.

Arduino:

#include <FastLED.h>
#include "SerialRecord.h"
SerialRecord reader(1);
int value;
#define NUM_LEDS 30  // How many leds on your strip?
#define DATA_PIN1 3
CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];
CRGB leds3[NUM_LEDS];
SerialRecord writer(1);
int state = 0;


void setup() {
  Serial.begin(9600);
  FastLED.addLeds<NEOPIXEL, DATA_PIN1>(leds1, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 5>(leds2, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 10>(leds3, NUM_LEDS);
}
void state0() {
  for (int i = 0; i < 30; i = i + 1) {
    leds1[i] = CRGB(0, 0, 0);
    leds2[i] = CRGB(0, 0, 0);
    leds3[i] = CRGB(0, 0, 0);
  }
  writer[0] = state;
  writer.send();
  delay(10);
  FastLED.show();
  if (reader.read()) {
    value = reader[0];
    //Serial.println(value);
    if (value == 1) {
      state = 2;
    }
    if (value == 2) {
      state = 3;
    }
    if (value == 3) {
      state = 4;
    }
  }
}
void state1() {
  for (int i = 0; i < 30; i = i + 1) {
    leds2[i] = CRGB(i, 10, 0);
    FastLED.show();
  }
  state =0;
}

void state2() {
  // LED1
  for (int i = 0; i < 30; i = i + 1) {
    leds1[i] = CRGB(10, 0, 0);
    FastLED.show();
    delay(50);
  }
  writer[0] = 1;
  writer.send();
  delay(10);
  state = 0;
}
void state3() {
  //LED2
  for (int i = 0; i < 30; i = i + 1) {
    leds2[i] = CRGB(0, 10, 0);
    FastLED.show();
    delay(50);
  }
  writer[0] = 2;
  writer.send();
  delay(10);
  state = 0;
}
//LED3
void state4() {
  for (int i = 0; i < 30; i = i + 1) {
    leds3[i] = CRGB(0, 0, 10);
    FastLED.show();
    delay(50);
  }
  writer[0] = 3;
  writer.send();
  delay(10);
  state = 0;
}
void loop() {
  if (state == 1) {
    state1();
  } else if (state == 2) {
    state2();
  } else if (state == 3) {
    state3();
  } else if (state == 4) {
    state4();
  } else {
    state0();
  }
  Serial.println(state);
}

Processing:

import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;


void setup(){
  size(600,600);
  //fullScreen();
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort,1);
}

int lastSend = 0;

void draw(){
  background(0);
  fill(255);
  stroke(0);
  circle(mouseX,mouseY,50);
}

void mousePressed(){
  if (mouseX<200 && mouseX>0){
      serialRecord.values[0]=1;
    } else if (mouseX>=200 && mouseX<400){
      serialRecord.values[0]=2;
    }else{
      serialRecord.values[0]=3;
    }
   //println(frameCount, serialRecord.values[0]);
   serialRecord.send();
}

It totally failed beacuse I couldn’t control the neopixels.

Maybe this was because of the complexity of the code, so I simplified the code and tried agian. This time the neopixels lighted up correctly, but it would went wrong if I clicked twice or more.

Arduino (simplified):

#include <FastLED.h>
#include "SerialRecord.h"
SerialRecord reader(3);
int value;
#define NUM_LEDS 58  // How many leds on your strip?
#define DATA_PIN1 3
CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];
CRGB leds3[NUM_LEDS];
//SerialRecord writer(3);
void setup() {
  Serial.begin(9600);
  FastLED.addLeds<NEOPIXEL, DATA_PIN1>(leds1, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 5>(leds2, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 10>(leds3, NUM_LEDS);
}
void loop() {
  if (reader.read()) {

    value = reader[0];
    //Serial.println(value);
    if (value==1) {
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i] = CRGB(10, i, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i <58; i = i + 1) {
        leds1[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
    } else if (value==2) {
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(i, 10, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
    } else if (value==3) {
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(i, 0, 10);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
    }
  }
}

I consulted Professor Gottfried for help, he changed the value I sent from a number to an array that store the times I clicked. The Arduino then do the if condition multiple times to fulfill the number in the array. 

Arduino:

#include <FastLED.h>
#include "SerialRecord.h"
SerialRecord reader(3);
int value;
#define NUM_LEDS 58  // How many leds on your strip?
#define DATA_PIN1 3
CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];
CRGB leds3[NUM_LEDS];

int completed[3];

void setup() {
  Serial.begin(9600);
  FastLED.addLeds<NEOPIXEL, DATA_PIN1>(leds1, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 5>(leds2, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 10>(leds3, NUM_LEDS);
}
void loop() {
  if (reader.read()) {

    value = reader[0];
    //Serial.println(value);
    if (completed[0] < reader[0]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i] = CRGB(10, i, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i <58; i = i + 1) {
        leds1[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[0]++;
    } else if (completed[1] < reader[1]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(i, 10, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[1]++;
    } else if (completed[2] < reader[2]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(i, 0, 10);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[2]++;
    }
  }
}

Processing: 

import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;

int[] count = new int[3];

void setup(){
  size(600,600);
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort,3);
}

int lastSend = 0;

void draw(){
  background(0);
  fill(255);
  stroke(0);
  circle(mouseX,mouseY,50);
  
  if (millis()-lastSend > 200) {
    serialRecord.write(count);
    printArray(count);
  }
}

void mousePressed(){
  if (mouseX<200 && mouseX>0){
     count[0]++;
    } else if (mouseX>=200 && mouseX<400){
      count[1]++;
    }else{
      count[2]++;
    }
}

This modification may seem unimportant, but if my purpose is to give my users an immersive and unforgettable experience, I need to allow them to click wherever and whatever they want. It proved that I was right when my classmates tried my project in the final testing. 

The laser cutting appeared more difficult than I thought. On one hand, I overestimate the hole I made for the wires, so I later used cardboard to seal the hole.

 

On the other hand, the sketch looked quite nice, but it turned out that there were two layers, therefore, some of the prominent parts were cut down. I had to use hot glue gun to stick them together. After the user testing, I found that the inside of the box was not dark enough to achive the desired effect, so I immediately made a front board.

I also attached many black papers inside and outside the box to make a room manually. The final result looked like this

Conclusions:

The purpose of my project is give users an unforgettable experiecen throught the exploration of light patterns of neopixels. I think my project has accomplished the goal. I initially expected them to slowly explore and view the pattern that neopixel displayed. But in the final testing, they clicked more often than I thought. Nevertheless, they all enjoyed the experience, which I believed was most valuable. I think it aligned with my definition of interaction——a conversation between two individuals (man or objects) that conveys messages with each other without limitation. First of all, the idea of exploration was certainly conveyed. Besides, seeing the neopixels light up in different pattern was also fantastic. If I were given more time, I would probably make another page after users click the mouse, so that they knew I want them to observe the light made by neopixels. I do think the interfacing part can be improved by integrating the screen with the box. As a result, the final project was more challenging and more intuitive than the midterm project. Since I didn’t have a partner this time, I clearly felt the hardship of doing all the job by one person. But I also learned how to laser cutting and how to perfect my code over and over again. I first made a mistake in the coding, but I soon viewed it from another angle and simplified the code. I think this was the turning point of my whole project. I’m really gratified to see how people interact with what I built. How to be interactive and creative is the greatest take-away of the project. At last, if I were to answer the question of “So what?” and “Why should anyone care?”, I would say that this project gives my users an enjoyable experience. Just as I got the inspiration from the Infinite Mirror Room, I also hope my project can bring inspirations to others. This is the meaning of my project.

Annex:

Final version Arduino:

#include <FastLED.h>
#include "SerialRecord.h"
SerialRecord reader(6);
int value;
#define NUM_LEDS 58  // How many leds on your strip?
#define DATA_PIN1 3
CRGB leds1[NUM_LEDS];
CRGB leds2[NUM_LEDS];
CRGB leds3[NUM_LEDS];

int completed[6];

void setup() {
  Serial.begin(9600);
  FastLED.addLeds<NEOPIXEL, DATA_PIN1>(leds1, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 5>(leds2, NUM_LEDS);
  FastLED.addLeds<NEOPIXEL, 10>(leds3, NUM_LEDS);
}
void loop() {
  if (reader.read()) {

    value = reader[0];
    //Serial.println(value);
    if (completed[0] < reader[0]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i] = CRGB(10, i, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i <58; i = i + 1) {
        leds1[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[0]++;
    } else if (completed[1] < reader[1]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(i, 10, 0);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[1]++;
    } else if (completed[2] < reader[2]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(10, 0, i);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds3[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[2]++;
    }else if (completed[3] < reader[3]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i]=CRGB(i,15,10);
        leds2[i] = CRGB(10, 10, i);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i]=CRGB(0,0,0);
        leds2[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[3]++;
    }else if (completed[4] < reader[4]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i]=CRGB(10,15,i);
        leds3[i] = CRGB(i, 10, 10);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds1[i]=CRGB(0,0,0);
        leds3[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[4]++;
    }else if (completed[5] < reader[5]) {
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i]=CRGB(15,i,0);
        leds3[i] = CRGB(10, i, 10);
        FastLED.show();
        delay(50);
      }
      for (int i = 0; i < 58; i = i + 1) {
        leds2[i]=CRGB(0,0,0);
        leds3[i] = CRGB(0, 0, 0);
        FastLED.show();
      }
      completed[5]++;
    }
  }
}

 Final version Processing:

import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;

int[] count = new int[6];

void setup(){
  fullScreen();
  //size(600,600);
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort,6);
}

int lastSend = 0;

void draw(){
  background(0);
  fill(255);
  stroke(0);
  circle(mouseX,mouseY,50);
  
  if (millis()-lastSend > 200) {
    serialRecord.write(count);
    printArray(count);
  }
}

void mousePressed(){
  if (mouseX<600 && mouseX>0){
    if (mouseY<500 && mouseY>=0){
     count[0]++;
    } else{
      count[1]++;
    }
    } else if (mouseX>=600 && mouseX<1200){
      if (mouseY<500 && mouseY>=0){
     count[2]++;
      } else{
      count[3]++;
      }
    }else{
      if (mouseY<500 && mouseY>=0){
     count[4]++;
    } else{
      count[5]++;
    }
    }
}

Work cited

Ernest Edmonds,Art, Interaction and Engagement, 2011, International    Conference on Information Visualization(IV), pp.451-459

IMA Recitation 10: Image & Video

In this week’s recitaton, I had to work individually to build a controller of media using Arduino and Processing. 

I first chose a video by Cheng CJ on https://www.pexels.com/, because it has the perfect length for my demonstration. I wanted to use two buttons to control the video. One for pause, the other for fast forward. When one of the button was pressed, its status changes from 0 to 1. Based on this, the code in processing will decide whether to pause(go fast forward) or not.

Something went wrong when I was building the circuit and the state of button didn’t change. I consult a IMA fellow and she changed the resistor’s position.

#include "SerialRecord.h"

// Change this number to send a different number of values
SerialRecord writer(2);
void setup() {
  Serial.begin(9600);
  pinMode(2,INPUT);
  pinMode(13,INPUT);
}

void loop() {
  int sensorValue1 = digitalRead(2);
  int sensorValue2=digitalRead(13);
  writer[0]=sensorValue1;
  writer[1] = sensorValue2;
  writer.send();

  // This delay slows down the loop, so that it runs less frequently. This can
  // make it easier to debug the sketch, because new values are printed at a
  // slower rate.
  delay(10);
}

In Arduino, I only use digitalRead to record the status of button and then send it to Processing.

import processing.video.*;
import processing.serial.*;
import osteele.processing.SerialRecord.*;
Serial serialPort;
SerialRecord serialRecord;
Movie myMovie;
int pvalue1;
int state1=1;
void setup() {
  size(600,600);
  myMovie = new Movie(this, "city.mp4");
  myMovie.loop();
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort,2);
}

void draw() {
  serialRecord.read();
  int value1 = serialRecord.values[0];
  int value2 = serialRecord.values[1];
  println(value1,value2,state1);
  if (myMovie.available()) {
    myMovie.read();
  }
  if (value1 != pvalue1){
    
  if (value1==1 && state1%2==1){
    myMovie.pause();
    state1=state1+1;
  } else if (value1==1 && state1%2==0){
    myMovie.play();
    state1=state1+1;
  }
  }
  pvalue1=value1;
  if (value2==1){
    myMovie.speed(2.0);
  } else{
    myMovie.speed(1.0);
  }
  image(myMovie, 0, 0,width, height);
}

As for the Processing part, the main problem is that every time I press the button, the variable state1 will add up for multiple times, so I cannot pause the video when pressed only once and continue when pressed again. So professor suggested I define a new variable for keeping the previous state and change the if condition outside, so that it only increase when it is different from the previous state.

The trick I mentioned above is certainly useful, but I nearly forgot it until professor reminded me. There are many more tricks to make coding easier, and all of those come from making mistakes again and again.