The 3d Tablet

I used two the infrared Wii Remotes to send the input of 3 axis’ to processing. I used this input to mount infrared LED’s to a glove and track the motion of two fingers in a 3d space. When the fingers pinch, the program starts drawing a line in 3d dimensions. The drawn lines can be rotated in 3d using a potentiometer, and erased by pressing any button. There is also a 2d drawing layer on top, for orthographic views. This could be a useful tool for designers, to quickly make rough 3d models. I want to remake it with stronger LED’s so that i could use a bigger space to draw in.

 

The code is quite buggy, for drawing, but tracks the fingers in 3 dimensions quite well.

import com.nootropic.processing.layers.*;
import processing.serial.*;
import oscP5.*;
import netP5.*;
import processing.opengl.*;

AppletLayers layers;
Serial myPort;
OscP5 osc;
OscP5 osc2;
ArrayList drawArray;
NetAddress MyBroadcastLocation;

float [] ir;
float [] ir2;
int points = 0;
float angle = 0.0;
float d = 170;
float c = 10;
float irx;
float iry;
float irz;
float ir2x;
float ir2z;

void setup() {

  size(1920, 1200, P3D);
  colorMode(HSB);
  frameRate(24);
  cursor(CROSS);

  ir = new float[12];
  ir2= new float[12];

  layers = new AppletLayers(this);
  layers.addLayer(new FLATlayer(this));

  osc = new OscP5(this,5600);
  osc2 = new OscP5(this,5700);

  MyBroadcastLocation = new NetAddress("127.0.0.1", 5600);

  drawArray = new ArrayList();

  osc.plug(this,"ir","/wii/irdata");
  osc.plug(this,"connected","/wii/connected");
  osc2.plug(this,"ir2","/wii/irdata");
  osc2.plug(this,"connected","/wii/connected");

  println(Serial.list());
 myPort = new Serial(this, Serial.list()[0], 9600);
 myPort.bufferUntil('\n');
 //reason for crash on finish
}

void paint(java.awt.Graphics g) {

  if (layers != null) {
    layers.paint(this);
  }
  else {
    super.paint(g);
  }
}

void ir(
float f10, float f11,float f12,
float f20,float f21, float f22,
float f30, float f31, float f32,
float f40, float f41, float f42
) {
  ir[0] = f10;
  ir[1] = f11;
  ir[2] = f12;
  ir[3] = f20;
  ir[4] = f21;
  ir[5] = f22;
  ir[6] = f30;
  ir[7] = f31;
  ir[8] = f32;
  ir[9] = f40;
  ir[10] = f41;
  ir[11] = f42;
}

void ir2(
float f10, float f11,float f12,
float f20,float f21, float f22,
float f30, float f31, float f32,
float f40, float f41, float f42
) {
  ir2[0] = f10;
  ir2[1] = f11;
  ir2[2] = f12;
  ir2[3] = f20;
  ir2[4] = f21;
  ir2[5] = f22;
  ir2[6] = f30;
  ir2[7] = f31;
  ir2[8] = f32;
  ir2[9] = f40;
  ir2[10] = f41;
  ir2[11] = f42;
}

void draw(){
  camera(
    cos(angle)*d, 10, sin(angle)*d,
    0, -10, 0,          0, -1, 0);
    background(255);
  noFill();
  box(75);
  loop();

  for (int i = drawArray.size()-1; i >= 0; i--) {
    SketchLayer lineDraw = (SketchLayer) drawArray.get(i);
    lineDraw.display();
  }
  if(keyPressed){
    if (key==CODED){
      if (keyCode==RIGHT){
        angle+=PI/300;
      }
      else if (keyCode==LEFT){
        angle -= PI/300;
      }
    }
    else{
      for (int i=0; i<drawArray.size(); i++) {
   drawArray.remove(i);
   break;
 }
    }
}

}

void loop(){
float irx = ir[1]*75-25;
float iry = ir2[1]*100-37;
float irz = -ir[0]*100+50;
float ir2x = ir[4]*75-25;
float ir2z = -ir[3]*100+50;
  //checks to see if the two points are close to one another.
    c = sqrt(pow(((irx) - (ir2x)),2) + pow(((irz) + (ir2z)),2));
    pushMatrix();
translate(irx, iry, irz);
sphere (0.5);
popMatrix();
translate(ir2x, iry,ir2z);
sphere(0.5);
if ( c<20){
  drawArray.add(new SketchLayer(irx, iry, irz,0,0,0));
 }

}

void serialEvent (Serial myPort) {
//rotation of scene from potentiometer input
 String inString = myPort.readStringUntil('\n');
  float inByte = float(inString);
  angle =inByte/300;
}

THEN THERE IS A SEPARATE CLASS FOR THE 2D LAYER

class FLATlayer extends Layer {

  FLATlayer(PApplet parent) {
    super(parent, P3D);
  }

  void setup() {
          background(0,0);
  }
void draw(){

  fill(0);
  if(mousePressed==true){
 line(mouseX,mouseY,pmouseX,pmouseY);
  }
  if (keyPressed==true){
if (key == ' '){
  background(0,0);
}
  }
  }
void loop(){

}
}
THEN THERE IS ANOTHER SEPARATE CLASS FOR THE LINES IN 3D 
class SketchLayer{
float x;
float y;
float z;
float px;
float py;
float pz;

SketchLayer(float tempx, float tempy,float tempz, float ptempx, float ptempy, float ptempz){
  x=tempx;
  y=tempy;
  z=tempz;
  px=ptempx;
  py=ptempy;
  pz=ptempz;
}
    void display() {
     fill(0);
     pushMatrix();
     line(x,y,z, px, py, pz);
     popMatrix();
  }

}

HammerFist 2000

For the final assignment, I chose to construct a carnival game. I picked the Hi Striker, a game where the player tries to send a weight flying up a string as far as possible by striking a lever with a hammer. This is what mine looks like:

Unfortunately, still a work in progress despite my best efforts.

Continue reading HammerFist 2000

Final Project

The technical impetus of my installation came from exploring augmented reality in processing. I wanted to do something with live video, but in a capacity only available to processing, rather than something I could already do in Max MSP. Augmented reality straddles a line of science fiction inevitability and general uselessness. It has the aesthetic of the future, but doesn’t really make much sense in any practical application.

The AR library I used was nyartoolkit, a derivative of the largely available artoolkit. Here’s the website:

http://nyatla.jp/nyartoolkit/wiki/index.php?FrontPage.en

 

The developers are japanese and as such, the code is commented in japanese, but it is the only AR processing library which works on mac. However, if you’re going to use it, you’ll have to add

try {
    quicktime.QTSession.open();
} catch (quicktime.QTException qte) {
    qte.printStackTrace();
}
to your setup() to get it to work.
Augmented Reality works by locating some kind of symbol bracketed inside a thick, black boarder. I ended up using one of the defaults, but you can find instructions on making your own in the data folder of the library
 I also used an OBJ loader found here code.google.com/p/saitoobjloader/ in order to use the structure I made in google sketchup.

Ultimately, I’m interested in in structures, specifically the proposition of parallel which conflate momentarilly conflate into singular systems. Imporatant in this is claiming a space for art which denies direct pragmatism and rather serves as a staging ground.

To repeat what I said during the critique, I believe there is space for a relational aesthetic which (through similar use of open space) draws from the pedanticisms and logics of art, rather than from those of the social and culturally broad. As traditional relational work produces the pragmatic staging of progress, the proposed form would produce the pragmatic occult.

For this to be done, the space needs to be aestheticized, in that each element needs to seem effected or imparted.I try to accomplish this by placing necessary technical pieces in awkward, inherently visible positions, trying to force the appearance function out of each element. This fundamentally unresolved space involves each element as specific and involved, rather than as a catalyst to the main technical subject. This effect is already inherent in viewing art in a gallery or university, I just wish to take it to its furthest point and collapse the systems of the art institution, the technological and the occult into miasma of relation experience, from which to be reconstructed as interceding parallels.

Here's the code:
/**	NyARToolkit for proce55ing/0.3.0
	(c)2008-2010 nyatla
	airmail(at)ebony.plala.or.jp
*/
//import fullscreen.*; 
import processing.video.*;
import jp.nyatla.nyar4psg.*;
import processing.opengl.*;
import javax.media.opengl.*;
import saito.objloader.*;    
//FullScreen fs; 
OBJModel model;   

Capture cam;
NyARBoard nya;
PFont font;

void setup() {
  	try {
		quicktime.QTSession.open();
	} catch (quicktime.QTException qte) {
    		qte.printStackTrace();
	}
  size(640,480,OPENGL);
  //fs = new FullScreen(this);
  //fs.enter();
 //fs.setShortcutsEnabled(true);
  colorMode(RGB, 100);
  font=createFont("FFScala", 32);
  model = new OBJModel(this, "ARpodium1.obj", "relative", QUADS);      //adjust the file name to your model's file name.
model.scale(155); 
//model.translateToCenter();
  //キャプチャを作成
  cam=new Capture(this,width,height);
  //平面検出クラスを作成
  //Left hand projection matrix
  nya=new NyARBoard(this,width,height,"camera_para.dat","patt.hiro",80); 
  print(nya.VERSION);
  //Right hand projection matrix
  //nya=new NyARBoard(this,width,height,"camera_para.dat","patt.hiro",80,NyARBoard.CS_RIGHT);

  //各種プロパティ設定(必要に応じて設定すること。何もしないとデフォルト値が入力される。)
  nya.gsThreshold=120;//画像2値化の閾値(0<n<255) default=110
  nya.cfThreshold=0.4;//変換行列計算を行うマーカ一致度(0.0<n<1.0) default=0.4
  //nya.lostDelay=10;//マーカ消失を無視する回数(0<n) default=10

  /*  他に、読み出しプロパティとして以下のものがある。
     これらはdetect関数がtrueを返した時に有効になる。
    double confidence
      マーカの一致度。(0<n<1.0)
    int lostCount
      マーカ認識後に消失した時に加算されるカウンタ。マーカの遅延消失に使用する。
    int pos2d[4][2]
      検出したマーカの画面上の頂点座標×4個
    PVector angle
      マーカの軸を中心にした回転角度(x,y,z)単位はラジアン
    PVector trans
      マーカの中心を基準とした平行移動量。単位はmm
    double transmat[12]
      OpenGLに指定するマーカの変換行列。
      行列から値を逆算する時、beginTransformを使用せずに自分で行列操作を行う時に使用してください。
    double projection[16]
      OpenGLに指定するProjection行列。
      beginTransformを使用せずに自分で行列操作を行う時に使用してください。
  */
}
//この関数は、マーカ頂点の情報を描画します。
//void drawMarkerPos(int[][] points)
//{
//  textFont(font,10.0);
//  stroke(100,0,0);
//  fill(100,0,0);
//  for(int i=0;i<4;i++){
//    ellipse(nya.pos2d[i][0], nya.pos2d[i][1],5,5);
//  }
//  fill(0,0,0);
//  for(int i=0;i<4;i++){
//    text("("+nya.pos2d[i][0]+","+nya.pos2d[i][1]+")",nya.pos2d[i][0],nya.pos2d[i][1]);
//  }
//}

String angle2text(float a)
{
  int i=(int)degrees(a);
  i=(i>0?i:i+360);
  return (i<100?"  ":i<10?" ":"")+Integer.toString(i);
}
String trans2text(float i)
{
  return (i<100?"  ":i<10?" ":"")+Integer.toString((int)i);
}

void draw() {
  background(255);
  if (cam.available() !=true) {
    return;
  }
  cam.read();
  //背景を描画
  hint(DISABLE_DEPTH_TEST);
  image(cam,0,0);
  hint(ENABLE_DEPTH_TEST);

  //マーカの検出。マーカが発見されるとdetectはTRUEを返す。
  if(nya.detect(cam)){
    hint(DISABLE_DEPTH_TEST);
    //一致度を書く
    textFont(font,25.0);
    fill((int)((1.0-nya.confidence)*100),(int)(nya.confidence*100),0);
    //text((int)(nya.confidence*100)+"%",width-60,height-20);
    //マーカの角度、水平位置等
//    pushMatrix();
//    textFont(font,10.0);
//    fill(0,100,0,80);
//    translate((nya.pos2d[0][0]+nya.pos2d[1][0]+nya.pos2d[2][0]+nya.pos2d[3][0])/4+50,(nya.pos2d[0][1]+nya.pos2d[1][1]+nya.pos2d[2][1]+nya.pos2d[3][1])/4+50);
//    text("TRANS "+trans2text(nya.trans.x)+","+trans2text(nya.trans.y)+","+trans2text(nya.trans.z),0,0);
//    text("ANGLE "+angle2text(nya.angle.x)+","+angle2text(nya.angle.y)+","+angle2text(nya.angle.z),0,15);
//    popMatrix();    
    //マーカの位置を描画
//    drawMarkerPos(nya.pos2d);
    hint(ENABLE_DEPTH_TEST);

  PGraphicsOpenGL pgl = (PGraphicsOpenGL) g;

    nya.beginTransform(pgl);//マーカ座標系での描画を開始する。
model.draw();
//    //ここからマーカ座標系
//    stroke(255,200,0);
//    translate(0,0,20);
//    box(40);
    nya.endTransform();//マーカ座標系での描画を終了する。(必ず呼んで!)
  }
}


Final Project: Tokyo Shinkansen (Bullet Train) LED Map – Part 2

Introduction

This is a continuation of assignment 5 which is the completion of the LED interactive map. Please see my previous post for background information

Implemented Changes

New Layout

This is the new layout I decided upon for the map. The boxes at the top represent the lanterns that I will use for the termini stations. The circle is the radius of the pointer attached to Tokyo (the hub station) and will point to the selected lines. The green, white and red buttons are the controls at the bottom of the unit. The Green button is the start button, the white button represents the potentiometer that will control the clock speed and the red button represents the stop button. I decided to keep all the station labels in Japanese text to maintain the authenticity of the layout.

New Screen Layout

I have made some additional changes since Assignment 5, in particular I have added a mode section to the control screen in processing. The mode section allows the user to choose between seeing all trains scheduled to stop at each station. Or the user can choose single train mode and see the timing between each specific station. I implemented the next station flashing pattern in the single train mode. That was the only way to get around the problem of having multiple trains shown and having the next station flashing to represent their arrival.

Putting it Together

After assignment 5, I continued to enhance the code in processing (the code is available at the end of this post) to include the next station flashing pattern, choosing a line, the servo motor code and the RGB LED for Tokyo station. I also had to debug other coding issues as well.

The biggest task was physically putting it together. I started off with a masonite board and drew out where I wanted each LED. I then drilled holes for the LEDs, buttons and potentiometer. Afterwards I spray painted it and started wiring underneath the board.

    

I used copper wiring for the ground connections and a 36 pin cable wire for the LED inputs to the Arduino. I then had to find a way to mount the control buttons and the servo motor onto the board and had to build small platforms to support them.

This is what the final board looks like:

Remaining Issues

When I was testing the servo motor component, the Arduino shut down each the servo moved too fast and too many times. I then discovered that the Arduino could not provide enough voltage to the servo motor. So in my final design, I allocated a separate power source to the servo motor. To stream line this and I also attached the input controls to the same separate power source which looking in hindsight was a mistake. This is because the servo motor produces a lot of noise on the power supply line and distorts the inputs going into the arduino. The potentiometer does not give accurate readings when the servo is running.

I also made an error in purchasing the correct switch. The red switch is actually a latch switch which means if you press it once, it will hold a state. I needed a momentary switch like the green one. By the time I realized this, the switch was already secured in place and soldered. Due to time constraints I was not able to replace the switch with a momentary one. Instead I made the red switch a toggle on and off switch for the program. The green switch has no function.

Another error I made was not grounding my switching inputs. Therefore I kept getting false signals during testing. But again, by the time I found out, everything was glued and soldered. The only way of working around this effectively was attaching a volt meter from the Arduino input to ground which in essence grounds the switch.

Conclusion

Overall the hardest challenge was physically putting together the project which (again) I under estimated the amount of time and things that could go wrong. But in the end, the project does work and I have included all the proposed changes in Assignment 5.

Code

/* Shinkansen.pde*/

import processing.serial.*;
import cc.arduino.*;
PFont timeFont;

Arduino arduino;

ArrayList Stations;

HScrollbar TimeScroll;

//Variable Speed clock variables
int LastTime = 0;
int VarSeconds;
int VarMinute;
int VarHour;
String TimeStamp;
String DisplayTime;

//Btn screen positions
int DirBtnHW = 108;
int DirBtnY = 246;
int ModeBtnY = 454;
int UpBtnX = 622;
int DownBtnX = 827;

int LargeBtnX = 92;
int LargeBtnH = 108;
int LargeBtnW = 312;

int TokyoOsakaBtnY = 246;
int TokyoNiigataBtnY = 370;
int TokyoHachinoheBtnY = 494;
int AllLinesBtnY = 627;

int TimeX = 622;
int TimeY = 730;

int TimeScrollX = 220;
int TimeScrollY = 615;

int TimeScrollW = 582;
int TimeScrollH = 31;

int StartPin = 9;
int StopPin = 10;
int TimeDialPin = 0;
int ServoPin = 8;
int ServoPosition;

int ClockSpeed;
final int MaxClockSpeed = 0;
boolean DownDirection;
boolean StartProgram;
boolean ScheduleMode;

int StartBtnValue;
int StopBtnValue;
int LastBtnValue;
long lastDebounceTime = 0;
long debounceDelay = 50;
String LineChosen;
int DelayTimer;
boolean FlashNextStation;

// Screen Images

PImage backgroundImg;
final int UpBtn = 0;
final int DownBtn = 1;
final int TokyoOsakaBtn = 2;
final int TokyoNiigataBtn = 3;
final int TokyoHachinoheBtn = 4;
final int AllLinesBtn = 5;
final int AllTrainsBtn = 6;
final int SingleTrainBtn = 7;
final String TokyoOsaka = “Red”;
final String TokyoNiigata = “Blue”;
final String TokyoHachinohe = “Yellow”;
final String AllLines = “All”;

final int MinDigitalPin = 22;
final int MaxDigitalPin = 53;
final int TokyoBluePin = 2;
final int TokyoRedPin = 4;
final int TokyoGreenPin = 5;

Buttons[] DisplayButtons = new Buttons [8];

void setup() {
// Setup screen and fonts
size (1024,768);
backgroundImg = loadImage(“background.png”);

DisplayButtons[TokyoOsakaBtn] = new Buttons(“TokyoOsakaBtn.png”, LargeBtnX, TokyoOsakaBtnY, LargeBtnW, LargeBtnH, TokyoOsakaBtn, true);
DisplayButtons[TokyoNiigataBtn] = new Buttons(“TokyoNiigataBtn.png”, LargeBtnX, TokyoNiigataBtnY, LargeBtnW, LargeBtnH, TokyoNiigataBtn, true);
DisplayButtons[TokyoHachinoheBtn] = new Buttons(“TokyoHachinoheBtn.png”, LargeBtnX, TokyoHachinoheBtnY, LargeBtnW, LargeBtnH, TokyoHachinoheBtn, true);
DisplayButtons[AllLinesBtn] = new Buttons(“AllLinesBtn.png”, LargeBtnX, AllLinesBtnY, LargeBtnW, LargeBtnH, AllLinesBtn, true);
DisplayButtons[UpBtn] = new Buttons(“InboundBtn.png”, UpBtnX, DirBtnY, DirBtnHW, DirBtnHW, UpBtn, true);
DisplayButtons[DownBtn] = new Buttons(“OutboundBtn.png”, DownBtnX, DirBtnY, DirBtnHW, DirBtnHW, DownBtn, true);
DisplayButtons[AllTrainsBtn] = new Buttons(“AllTrainsBtn.png”, UpBtnX, ModeBtnY, DirBtnHW, DirBtnHW, AllTrainsBtn, true);
DisplayButtons[SingleTrainBtn] = new Buttons(“SingleTrainBtn.png”, DownBtnX, ModeBtnY, DirBtnHW, DirBtnHW, SingleTrainBtn, true);

timeFont = loadFont(“MyriadPro-Regular-69.vlw”);

//record the time that application starts
VarSeconds = second();
VarMinute = minute();
VarHour = hour();
TimeStamp = null;

DelayTimer = millis();

Stations = new ArrayList();
//Load the file
InitializeData();

// setup arduino board
println(Arduino.list());
arduino = new Arduino(this, Arduino.list()[1], 57600);

// set digital inputs
arduino.pinMode(StartPin, arduino.INPUT);
arduino.pinMode(StopPin, arduino.INPUT);
arduino.pinMode(ServoPin, arduino.SERVO);
// set PWM outputs
for (int i = 2; i<= 8; i++)
arduino.pinMode(i, Arduino.OUTPUT);

// set Digital Outputs
for (int i = MinDigitalPin; i <= MaxDigitalPin; i++)
arduino.pinMode(i, Arduino.OUTPUT);

}

void draw () {
//load background image
noTint();
image(backgroundImg, 0, 0);

//load buttons
for (int i = 0; i < DisplayButtons.length; i ++) {
DisplayButtons[i].display();
}

//load scroll bar
//TimeScroll.display();

CheckBtns();

if (StartProgram == true) {
UpdateClock();

//if the TimeStamp is null take a snapshot
if (TimeStamp == null) {
TimeStamp = DisplayTime;
}

if (ScheduleMode == true) {
CheckSchedule();
}
else {
CheckTravelTime();
}
}
// if program stops, assign null value to timestamp
else {
TimeStamp = null;
}

}

void CheckBtns() {

//Check potientiometer in arduino here for time

ClockSpeed = int(map(arduino.analogRead(TimeDialPin), 0, 1023, 1000, MaxClockSpeed));
print(“Clock Speed: “);
println(ClockSpeed);

//Check for start and stop button here
StartBtnValue = arduino.digitalRead(StartPin);
StopBtnValue = arduino.digitalRead(StopPin);

//print(“Start Button value: “);
//println(StartBtnValue);

print(“Stop Button value: “);
println(StopBtnValue);
/*
if (StartBtnValue == arduino.HIGH) {
StartProgram = true;
StartBtnValue = 0;
} else if (StopBtnValue == arduino.HIGH) {
StartProgram = false;
StopBtnValue = 0;
for (int j=2; j <= 8; j++) {
arduino.digitalWrite(j, Arduino.LOW);
}
for (int j = MinDigitalPin; j <= MaxDigitalPin; j++) {
arduino.digitalWrite(j, Arduino.LOW);
}
}*/

//if (StartBtnValue != LastBtnValue) {
//  lastDebounceTime = millis();
//}

//if ((millis() – lastDebounceTime) > debounceDelay) {

if (StopBtnValue == arduino.HIGH) {

//if (StartProgram == false) {
StartProgram = true;
//}
}

else if (StopBtnValue == arduino.LOW) {
StartProgram = false;
for (int j=2; j <= 8; j++) {
arduino.digitalWrite(j, Arduino.LOW);
}
for (int j = MinDigitalPin; j <= MaxDigitalPin; j++) {
arduino.digitalWrite(j, Arduino.LOW);
}

}
//}
//LastBtnValue = StopBtnValue;

//print(“Start Program: “);
//println(StartProgram);

//println(ClockSpeed);

for (int i = 0; i < DisplayButtons.length; i ++) {
DisplayButtons[i].CheckRollOver();
if (DisplayButtons[i].Clicked == true) {

switch(i) {
case UpBtn:

DisplayButtons[DownBtn].Clicked = false;
DownDirection = false;

break;
case DownBtn:

DisplayButtons[UpBtn].Clicked = false;
DownDirection = true;

break;
case TokyoOsakaBtn:
DisplayButtons[TokyoNiigataBtn].Clicked = false;
DisplayButtons[TokyoHachinoheBtn].Clicked = false;
DisplayButtons[AllLinesBtn].Clicked = false;
LineChosen = TokyoOsaka;
break;
case TokyoNiigataBtn:
DisplayButtons[TokyoOsakaBtn].Clicked = false;
DisplayButtons[TokyoHachinoheBtn].Clicked = false;
DisplayButtons[AllLinesBtn].Clicked = false;
LineChosen = TokyoNiigata;
break;
case TokyoHachinoheBtn:
DisplayButtons[TokyoNiigataBtn].Clicked = false;
DisplayButtons[TokyoOsakaBtn].Clicked = false;
DisplayButtons[AllLinesBtn].Clicked = false;
LineChosen = TokyoHachinohe;
break;
case AllLinesBtn:
DisplayButtons[TokyoNiigataBtn].Clicked = false;
DisplayButtons[TokyoOsakaBtn].Clicked = false;
DisplayButtons[TokyoHachinoheBtn].Clicked = false;
LineChosen = AllLines;
break;
case AllTrainsBtn:
DisplayButtons[SingleTrainBtn].Clicked = false;
ScheduleMode = true;
break;
case SingleTrainBtn:
DisplayButtons[AllTrainsBtn].Clicked = false;
ScheduleMode = false;
break;
}
}
}
}

void InitializeData() {
String[] LineData = loadStrings(“Data.txt”);
DownDirection = false;
StartProgram = false;
FlashNextStation = false;
ScheduleMode = true;
LastBtnValue = arduino.LOW;

DisplayButtons[AllTrainsBtn].Clicked = true;
DisplayButtons[SingleTrainBtn].Clicked = false;

DisplayButtons[UpBtn].Clicked = true;
DisplayButtons[DownBtn].Clicked = false;

LineChosen = TokyoOsaka;

DisplayButtons[TokyoOsakaBtn].Clicked = true;
DisplayButtons[TokyoNiigataBtn].Clicked = false;
DisplayButtons[TokyoHachinoheBtn].Clicked = false;
DisplayButtons[AllLinesBtn].Clicked = false;
ClockSpeed = 1000;

//TimeScroll = new HScrollbar(TimeScrollX, TimeScrollY, TimeScrollW, TimeScrollH, 1);

//go through each line
for (int i = 0; i < LineData.length; i++) {
// each line contains Staiton Name and Times, create Array with this
String[] StationData = splitTokens(LineData[i]);
//println(StationData);
boolean Direction;

// get the station name
String Colour = StationData[0];
if (int(StationData[1]) == 1) {
Direction = true;
} else {
Direction = false;
}

String StationName = StationData[3];
int PinNumber = int(StationData[2]);

boolean LastStation;
if (int(StationData[4]) == 1) {
LastStation = true;
} else {
LastStation = false;
}

//extract travel times
String TravelTime = StationData[5];

//println(Colour + ” “+ Direction + ” “+ StationName);

// storet he times in a new array
String[] Time = new String[StationData.length-6];
//println(Time.length);

for (int j = 0; j < Time.length; j++) {
Time[j] = StationData[j+6];
}
println(Time);
// Store station name and times in a class that will split hours and minutes
Station s = new Station(StationName, Time, Direction, Colour, PinNumber, LastStation, TravelTime);
println(s.LineColour + ” “+ s.DownDirection + ” “+ s.Name + ” ” + s.LastStation);
for (int k = 0; k < s.Hours.length; k++) {
println(s.Hours[k] + “:” + s.Minutes[k]);
}

Stations.add(s);
}

}

void CheckSchedule() {

for (int i = 0; i < Stations.size(); i ++) {

// Get Current station
Station station = (Station) Stations.get(i);

// If schedule mode then turn LED on or off based on schedule

if (LineChosen.equals(station.LineColour) == true || LineChosen.equals(AllLines) == true) {
//SetServoPosition(station.LineColour);
// check to see if down direction button pressed
if (station.DownDirection == DownDirection) {
// scroll through each time at each station and check to see if it matches
for (int j = 0; j < station.Hours.length; j++) {

if (station.Hours[j] == VarHour && station.Minutes[j] == VarMinute) {

if (station.LEDOn[j] == false) {

if (station.Name.equals(“Tokyo”) == true) {
SetTokyoColour(station.LineColour);
}

arduino.digitalWrite(station.PinNumber, Arduino.HIGH);

station.LEDOn[j] = true;
println (“Index: ” + j + ” ” + station.Name + ” ” + station.LineColour + ” ” + station.DownDirection +  ” LED ” + station.PinNumber +” ON – Time – ” + station.Hours[j] + “:” + station.Minutes[j]);

}

} else {

if (station.LEDOn[j] == true) {

arduino.digitalWrite(station.PinNumber, Arduino.LOW);
station.LEDOn[j] = false;

println (“Index: ” + j + ” ” + station.Name + ” ” + station.LineColour + ” ” + station.DownDirection +  ” LED ” + station.PinNumber +” OFF – Time – ” + station.Hours[j] + “:” + station.Minutes[j]);
//noLoop();
}

}
}
}
}
}
}

void SetServoPosition(String LineColour) {
/*if(LineColour.equals(TokyoOsaka) == true) {

arduino.analogWrite(ServoPin, 200);
}
else if (LineColour.equals(TokyoNiigata) == true) {

arduino.analogWrite(ServoPin, 127);
}

else if (LineColour.equals(TokyoHachinohe) == true) {

arduino.analogWrite(ServoPin, 255);
}*/
}

void SetTokyoColour(String LineColour) {
// pick LED and move position of servo motor

if(LineColour.equals(TokyoOsaka) == true) {

arduino.analogWrite(TokyoRedPin, 255);
arduino.analogWrite(TokyoBluePin, 0);
arduino.analogWrite(TokyoGreenPin, 255);
if(ServoPosition != 120) {
arduino.analogWrite(ServoPin, 120);
ServoPosition = 120;
}
}
else if (LineColour.equals(TokyoNiigata) == true) {

arduino.analogWrite(TokyoRedPin, 255);
arduino.analogWrite(TokyoBluePin, 255);
arduino.analogWrite(TokyoGreenPin, 0);
if (ServoPosition != 165) {
arduino.analogWrite(ServoPin, 165);
ServoPosition = 165;
}
}

else if (LineColour.equals(TokyoHachinohe) == true) {
arduino.analogWrite(TokyoRedPin, 0);
arduino.analogWrite(TokyoBluePin, 255);
arduino.analogWrite(TokyoGreenPin, 255);
if (ServoPosition != 230) {
arduino.analogWrite(ServoPin, 215);
ServoPosition = 230;
}
}

}

void CheckTravelTime() {
String [] TimeStampArray;
String [] CurrentTimeArray;

int StampSeconds;
int StampMinutes;
int StampHours;

int CurrentSeconds;
int CurrentMinutes;
int CurrentHours;

int SecondsDiff;
int MinutesDiff;
int HoursDiff;

int TimeDiff;

for (int i = 0; i < Stations.size(); i ++) {

// Get Current station
Station station = (Station) Stations.get(i);
//println(“Station: ” + station.Name + ” Last Station?: ” + station.LastStation);
// If schedule mode then turn LED on or off based on schedule

if (LineChosen.equals(station.LineColour) == true || LineChosen.equals(AllLines) == true) {

//SetServoPosition(station.LineColour);
// check to see if down direction button pressed
if (station.DownDirection == DownDirection) {
// scroll through each time at each station and check to see if it matches

//Check how much time has passed since time stamp
//Parse the string
TimeStampArray = split(TimeStamp, “:”);
StampSeconds = int(TimeStampArray[2]);
StampMinutes = int(TimeStampArray[1]);
StampHours = int(TimeStampArray[0]);

print(“Time Stamp: “);
println(TimeStampArray[0] + “:” + TimeStampArray[1] + “:” + TimeStampArray[2]);

CurrentTimeArray = split(DisplayTime, “:”);
CurrentSeconds = int(CurrentTimeArray[2]);
CurrentMinutes = int(CurrentTimeArray[1]);
CurrentHours = int(CurrentTimeArray[0]);

print(“Current Time: “);
println(CurrentTimeArray[0] + “:” + CurrentTimeArray[1] + “:” + CurrentTimeArray[2]);

//Calculate difference in seconds (may not need this)
/*if (StampSeconds > CurrentSeconds) {
SecondsDiff = 60 – StampSeconds + CurrentSeconds;
}
else {
SecondsDiff = CurrentSeconds – StampSeconds;
}*/

//Calculate difference in hours

if (StampHours > CurrentHours) {
HoursDiff = (24 + CurrentHours) – StampHours;
}
else {
HoursDiff = CurrentHours – StampHours;
}

//Calculate difference in minutes
if (StampMinutes > CurrentMinutes) {
MinutesDiff = 60 – StampMinutes + CurrentMinutes;
//need to carry the “1”
HoursDiff –;
}
else {
MinutesDiff = CurrentMinutes – StampMinutes;
}

TimeDiff = MinutesDiff + (HoursDiff * 60);

// If the time difference matches current station make current LED station solid, otherwise flash next station LED

if (station.LastStation == false) {
Station nextStation = (Station) Stations.get(i+1);
if (TimeDiff > int(station.TravelTime) && TimeDiff < int(nextStation.TravelTime)) {
//flash current station

if (nextStation.Name.equals(“Tokyo”) == true) {
SetTokyoColour (nextStation.LineColour);
}

FlashLED(nextStation.PinNumber);
arduino.digitalWrite(station.PinNumber, Arduino.LOW);
println(“Flashing LED: ” + nextStation.Name);

}
else if (TimeDiff == int(station.TravelTime)) {
//if (station.LEDOn[j] == false) {

if (station.Name.equals(“Tokyo”) == true) {
SetTokyoColour(station.LineColour);
}
arduino.digitalWrite(station.PinNumber, Arduino.HIGH);

//station.LEDOn[j] = true;
println (“Travel Time: ” + station.TravelTime + ” ” + station.Name + ” ” + station.LineColour + ” ” + station.DownDirection +  ” LED ” + station.PinNumber);// +” ON – Time – ” + station.Hours[j] + “:” + station.Minutes[j]);
//}
//If the Time differen is greater than 3 hours then reset the time stamp to current time

}
//else {
//  arduino.digitalWrite(station.PinNumber, Arduino.LOW);
//}
}
else {
if (TimeDiff > int(station.TravelTime)) {
arduino.digitalWrite(station.PinNumber, Arduino.LOW);
TimeStamp = DisplayTime;
}
}

print(“Time Difference: “);
println(TimeDiff);

}
}
}

}

void FlashLED(int PinNumber) {
int DelayValue;

DelayValue = int(map(ClockSpeed, 1000, MaxClockSpeed, 500, 20));

if ((millis() – DelayTimer) < DelayValue)
{
arduino.digitalWrite(PinNumber, Arduino.HIGH);
}
else if((DelayValue*2) > (millis() – DelayTimer) && ((millis()-DelayTimer) >DelayValue)){
arduino.digitalWrite(PinNumber, Arduino.LOW);
}
else if((DelayValue*2)<(millis() – DelayTimer))
{
DelayTimer = millis();
}
}

/*void FlashLED(int PinNumber, boolean Flash) {
/*arduino.digitalWrite(PinNumber, Arduino.HIGH);
delay(5);
arduino.digitalWrite(PinNumber, Arduino.LOW);
delay(5);
if (Flash == true) {
if ((millis() – DelayTimer) < 500)
{
arduino.digitalWrite(PinNumber, Arduino.HIGH);
}
else if(1000 > (millis() – DelayTimer) && ((millis()-DelayTimer) >500)){
arduino.digitalWrite(PinNumber, Arduino.LOW);
}
else if(1000<(millis() – DelayTimer))
{
DelayTimer = millis();
}
}
}*/

void UpdateClock () {

String DisplayHours;
String DisplayMinutes;

String DisplaySeconds;

if (VarMinute >= 0 && VarMinute <=9) {
DisplayMinutes = “0” + str(VarMinute);
} else {
DisplayMinutes = str(VarMinute);
}
if (VarHour >=0 && VarHour <=9) {
DisplayHours = “0” + str(VarHour);
} else {
DisplayHours = str(VarHour);
}
if (VarSeconds >=0 && VarSeconds <=9) {
DisplaySeconds = “0” + str(VarSeconds);
} else {
DisplaySeconds = str(VarSeconds);
}

DisplayTime = DisplayHours + “:” + DisplayMinutes + “:” + DisplaySeconds;
textFont(timeFont, 69);
fill(#FFFFFF);
text(DisplayTime, TimeX,TimeY);
println(VarHour + “:” + VarMinute + “:” + VarSeconds);

if(millis() – LastTime > ClockSpeed ){
LastTime = millis();

if (ClockSpeed <= 1000 && ClockSpeed >= 750) {
VarSeconds++;
//println(“normal increase”);
} else if (ClockSpeed < 750 && ClockSpeed >= 500) {
VarSeconds+=15;
} else if (ClockSpeed < 500 && ClockSpeed >= 250) {
VarSeconds+=30;
//println(“fast increase”);
} else if (ClockSpeed < 250 && ClockSpeed >= 0) {
VarSeconds+=60;
//println(“super fast”);
}

if (VarSeconds > 59) {
VarSeconds = 0;
VarMinute++;

if (VarMinute > 59) {
VarMinute = 0;
VarHour++;

if (VarHour > 23) {
VarHour = 0;
}
}
}
}
}

/*Buttons.pde*/

class Buttons

{
PImage BtnImg;
String FileName;
int BtnX, BtnY;
int BtnW, BtnH;
int Index;
boolean Clicked;
boolean Display;

Buttons (String FileName, int BtnX, int BtnY, int BtnW, int BtnH, int Index, boolean Display) {

this.FileName = FileName;
this.BtnX = BtnX;
this.BtnY = BtnY;
this.BtnW = BtnW;
this.BtnH = BtnH;
this.Index = Index;
this.Display = Display;

BtnImg = loadImage(FileName);
Clicked = false;

}

void display () {
if (Display == true) {
image(BtnImg, BtnX, BtnY);
}
}

void CheckRollOver() {

if (mouseX >= BtnX && mouseX <= BtnX+BtnW && mouseY >= BtnY && mouseY <= BtnY+BtnH) {

if (Clicked == false) {
tint(#ED9997);
display();
println(“within area Index: ” + Index);
} else {
tint(#FF0000);
display();
}

if (Clicked == false) {

if (mousePressed) {
// if any buttons are pressed, ensure all LED’s are off before turning on other ones
for (int j = MinDigitalPin; j <= MaxDigitalPin; j++) {
arduino.digitalWrite(j, Arduino.LOW);
}
tint(#990000);
display();
if (Clicked == false) {
Clicked = true;
println(“Clicked = ” + Clicked);
} else {
Clicked = false;
println(“Clicked = ” + Clicked);
// return value
}
}
}

} else {

if (Clicked == true) {
//println(“hold red”);
tint(#FF0000);
display();

} else {
noTint();
display();
}
}
}
}

/*Stations.pde*/

class Station {
String Name;
int[] Hours;
int[] Minutes;
String[] Time;
boolean DownDirection;
String LineColour;
int PinNumber;
boolean[] LEDOn;
boolean LastStation;
String TravelTime;

Station (String Name, String[] Time, boolean DownDirection, String LineColour, int PinNumber, boolean LastStation, String TravelTime) {

this.Name = Name;
this.DownDirection = DownDirection;
this.LineColour = LineColour;
this.PinNumber = PinNumber;
this.LastStation = LastStation;
this.TravelTime = TravelTime;
//this.LEDOn = LEDOn;
/*println(this.Name);
println(this.DownDirection);
println(LineColour);*/
Hours = new int[Time.length];
Minutes = new int[Time.length];
LEDOn = new boolean[Time.length];

for (int i = 0; i < Time.length; i ++) {
/*if(Time[i] == “|”) {
Hours[i] = 99;
Minutes[i] = 99;
LEDOn[i] = false;
}*/

//else {
this.Time = split(Time[i], “:”);
Hours[i] = int(this.Time[0]);
Minutes[i] = int(this.Time[1]);
LEDOn[i] = false;
//}
//println(Hours[i] + “:” + Minutes[i]);
}

}
}

 

 

SOS Paper Boy

For this assignment, we had to use sensors in tandem with a servo motor. I had never really worked with a servo motor before, so I wasn’t really sure how it was supposed to be used. I knew three things about it: 1) It could spin, 2) you can attach things to it, and 3) it’s pretty noisy. After playing around with it and having it do things while placed in various positions, I came up with the idea of a little person trapped in a dark box.

Here is a video of it:

The original idea was for there to be a speaker inside the box as well. The speaker would play out the morse code for “SOS” (… _ _ _  …) while the box was closed, and when it opened you’d see the little guy inside doing his happy dance. However, there was some sort of conflict between the arduino libraries for the tone and servo, which would have required me rewriting parts of the libraries to make it work. I would’ve loved to, but I had no idea where to start with rewriting a library, so I compromised and instead had the servo make the SOS signal instead by timing its movements. If you listen closely, you’ll notice that only 2 of the 3 dots for the first S sound, so it sounds something like .. _ _ _ … instead of … _ _ _ … I tried to compensate by putting in the code for 4 dots instead of 3, but that did not work, and I really have no clue how it might be fixed.

If I had the chance to do this differently, I might have chosen to do something more sophisticated with the servo motor, having seen others’ projects and now being more familiar with its capabilities.

Code for SOS Paper Boy:

#include <Servo.h>
Servo myservo;

int pos = 0;
int maxPos = 90;
int delayServo = 1;
int letterS = 1;
int letterO = 8;
int lightSensorPin = A0;
int sensorValue = 1;
int afterLetters = 700;
int afterLettero = 700;

void setup() {
  myservo.attach(9);
  Serial.begin(9600);
  pinMode(lightSensorPin, INPUT);
}

void loop() {
  sensorValue = analogRead(lightSensorPin);
  if (sensorValue > 400) {
    Serial.println(sensorValue);
    for(pos >= 1; pos < maxPos; pos +=1) {
      myservo.write(pos);
      delay(delayServo);
    }
    for(pos = maxPos; pos >= 1; pos -=1) {
      myservo.write(pos);
      delay(delayServo);
    }
  }
  else {
    Serial.println(sensorValue);
    for(pos >= 1; pos < maxPos; pos += 1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(afterLetters);
    for(pos = maxPos; pos >= 1; pos -= 1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(afterLetters);
    for(pos >= 1; pos < maxPos; pos += 1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(afterLetters);
    for(pos = maxPos; pos >= 1; pos -=1) {
      myservo.write(pos);
      delay(letterO);
    }
    delay(afterLettero);
    for(pos >= 1; pos < maxPos; pos += 1) {
      myservo.write(pos);
      delay(letterO);
    }
    delay(afterLettero);
    for(pos = maxPos; pos >= 1; pos -= 1) {
      myservo.write(pos);
      delay(letterO);
    }
    delay(afterLettero);
    for(pos >= 1; pos < maxPos; pos += 1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(afterLetters);
    for(pos = maxPos; pos >= 1; pos -=1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(afterLetters);
    for(pos >= 1; pos < maxPos; pos += 1) {
      myservo.write(pos);
      delay(letterS);
    }
    delay(2500);
  }
}

Danger Bar

For this Processing + Arduino assignment, I chose to recreate the aesthetic of a ‘danger bar’, a gauge that would go from green to red, and would be controlled via the x-coordinate of the user’s cursor.

Here is a quick video of it at work:

After setting up the danger bar in Processing came the trickier part, which was rigging the gauge to work with a bunch of LEDs. The idea was that when the gauge was in the “safe zone”, only the green lights would flash, then when the gauge was in the “danger zone”, both the green AND the red lights would flash. The first problem became immediately apparent when the file was run for the first time; the addition of the LEDs gave the gauge awful, awful lag. To combat this, I thought to declare an array for the LEDs hoping that would help. It didn’t.

After that, I experimented a little and decided to see how the number of LEDs at work correlated with the program’s speed. With only one LED set to do anything, it ran much better, so I decided to declare two different arrays and have the program deal with only four LEDs at any given time, the green ones for the safe zone, and only the red ones for the danger zone.

Here it is a video of it (there is still a lot of lag present, but less than before):

It may be worth mentioning that I spent a significant amount of time trying to figure out how to get arrays to work. In the video, you might notice that one LED out of each array did not respond, however, I fixed that little mishap and the code below has the fix so all the LEDs work.

Code used for the danger bar:

import processing.serial.*;
import cc.arduino.*;

Arduino arduino;
int x = 0;
int bgValue = 1200;

void setup() {
  size(1200,400);
  frameRate(600000);
}
  
void draw() {
  background(bgValue-mouseX,bgValue-mouseX,bgValue-mouseX);
  fill(-700 + mouseX,1250-mouseX,0);
  rect(0,125,mouseX,150);
  if(mouseX>=1100) {
    background(255 - x*2);
    x = x + 2;
    delay(1250-mouseX);
    x = x * -1;
    fill(255,0,0);
    rect(0,125,mouseX,150);
  }
}

Code for the danger bar + LEDs:

import processing.serial.*;
import cc.arduino.*;

Arduino arduino;
int greenArray[] = {6,7,8,9};
int redArray[] = {2,3,4,5};
int bgValue = 1200;
int shortDelay = 50;
int longDelay = 200;
int greenCount = 0;
int redCount = 0;
int x = 0;

void setup() {
  arduino = new Arduino(this, Arduino.list()[0], 57600);
  for (greenCount = 0; greenCount < 4; greenCount++) {
    arduino.pinMode(greenArray[greenCount], Arduino.OUTPUT);
  }
  for (redCount = 0; redCount < 4; redCount++) {
    arduino.pinMode(redArray[redCount], Arduino.OUTPUT);
  }
  size(1200,400);
}

void draw() {
  background(bgValue-mouseX,bgValue-mouseX,bgValue-mouseX);
  fill(-700 + mouseX,1250-mouseX,0);
  rect(0,125,mouseX,150);
  if(mouseX>=1100) {
    background(255 - x*2);
    x = x + 2;
    delay(50);
    x = x * -1;
    fill(255,0,0);
    rect(0,125,mouseX,150);
    for (redCount = 0; redCount < 4; redCount++) {
      arduino.digitalWrite(redArray[redCount], Arduino.HIGH);
      delay(shortDelay);
      arduino.digitalWrite(redArray[redCount], Arduino.LOW);
      delay(shortDelay);
    }
  }
  if(mouseX<1100) {
    for (greenCount = 0; greenCount < 4; greenCount++) {
      arduino.digitalWrite(greenArray[greenCount], Arduino.HIGH);
      delay(longDelay);
      arduino.digitalWrite(greenArray[greenCount], Arduino.LOW);
      delay(longDelay);
    }
  }
}

The automatic remote control

Servo and Light Meter Wiring

This is a very simple program that uses a servo motor and a light sensor. Whenever the lights are turned on in the room, the servo motor turns and presses the power button. To do this, i roughly read the values of the light sensor when the lights in the room are on and off, and i used greater than and less than arguments to assign a value to the boolean “lightsOn”. Whenever the lightsON boolean was active, the servo turns to press the remote. The video can be found HERE

Button/Potentiometer-Controlled LED’s

The Wiring

I used my button from the previous project to control a sequence of LED’s. The LED’s had two modes. One in which only the green LED would light up constantly, and the second, where the green would flash, the red would flash, and then the yellow would flash, and they would repeat. I used the potentiometer to control the amount of time that the LED’s blinked, and the amount of time they waited between lights.I have a video of the LED’s being tested: http://youtu.be/4XWSnK7nPpY

Home-made Button

This is the button i made using cardboard, a spring, a staple and two exacto blades:

A simple instance button

The spring keeps the two plates separated, and when enough pressure is applied, the bent staple presses up against the exact blade, completing the circuit.

The Interactive Building Block

Prototype

An interactive tool for learning and playing.

The building block has a small screen in the top that can show small images. It is also touch sensitive, although not location sensitive, but binary. On their own, the cubes do not serve much purpose other than a clock or a fridge magnet, but when combined with other blocks, they will interact with each other to make a full experience. For example: the blocks could be programmed to play tick-tack-toe, or hangman, where every block can be it’s own x, o, or other letter. Large puzzles could be made, depending on how many blocks there are. There are many possibilities for games: almost any traditional board game, card game, or paper-based game could be done with these blocks, given there are enough of them. People could store all their information and contacts on one, and when they meet someone with another, they can bump them together and trade information.

A dock could be built to exchange information quickly to a computer, making it a quick-connect USB key as well. It could also be updated in an app-like manor to place new games and new versions onto the device.

 

The design is a basis for many possibilities to rise from it. The modular design allows a wide range of capabilities, and flexibility of use.