Kelvin Chan's profile

IAT320 - Advance Tangible Object

The above two images display the two stage of ideation and concept design sketches the project went through. The initial concept design for the project involved fully utilizing the degrees of movement that could be calculated using an IMU 9 degrees of freedom sensor as well as wireless comunication between the tangible object and the display screen. However problems involving the technical skills required for the desired outcome was to difficult to fully incorporate within the given timeframe. As such the project required some adjustment and simplification of some design components. 
 
The second concept sketch that was used for the final prototype involved removing the wireless capabilities of the design due to time constraints. As such the range of free movement was significantly decreased due to the need for a wired connection to the display. Furthermore the amount of display options was decreased by removing the spiralling effect, while combining the idea of rotation input with the up and down motion output. However despite the simplification of the design and movements, compared to the initial sketch, the final prototype was sufficient in representing the project's concept.
Components:
 
The image above shows the hardware and wiring for the project. The components used in he final prototype is a laptop, Arduino Uno, Sparkfun's 9DOF Sensor Stick. The image in the upper right hand corner is that of the Dragino Yun Shield, that was considered in the initial ideation and design phase to allow for wireless connection between the object and display, however was to complicated to incorporate in the timeframe provided and as such was excluded from the final prototype. The software used for the project is Arduino for the sensors code and Processing for the display code.
Code:
 
//references for the code:
//https://chionophilous.wordpress.com/2012/02/10/connecting-to-sparkfuns-9dof-sensor-stick-i2c-access-to-adxl345-itg-3200-and-hmc5843/
//https://learn.sparkfun.com/tutorials/connecting-arduino-to-processing
//https://forum.processing.org/one/topic/new-to-processing-and-need-help-with-ripple-effect-urgent.html
 
//Arduino:
#include <Wire.h>
void i2c_write(int address, byte reg, byte data) {
  // Send output register address
  Wire.beginTransmission(address);
  Wire.write(reg);
  // Connect to device and send byte
  Wire.write(data); // low byte
  Wire.endTransmission();
}
void i2c_read(int address, byte reg, int count, byte* data) {
 int i = 0;
 // Send input register address
 Wire.beginTransmission(address);
 Wire.write(reg);
 Wire.endTransmission();
 // Connect to device and request bytes
 Wire.beginTransmission(address);
 Wire.requestFrom(address,count);
 while(Wire.available()){ // slave may send less than requested 
   char c = Wire.read(); // receive a byte as character
   data[i] = c;
   i++;
   }
 Wire.endTransmission();
}
#define  ADXL345_ADDRESS (0xA6 >> 1)
//There are 6 data registers, they are sequential starting 
//with the LSB of X.  We'll read all 6 in a burst and won't
//address them individually
#define ADXL345_REGISTER_XLSB (0x32)
 //Need to set power control bit to wake up the adxl345
 #define ADXL_REGISTER_PWRCTL (0x2D)
 #define ADXL_PWRCTL_MEASURE (1 << 3)
 void init_adxl345() {
  byte data = 0;
  i2c_write(ADXL345_ADDRESS, ADXL_REGISTER_PWRCTL, ADXL_PWRCTL_MEASURE);
  //Check to see if it worked!
  i2c_read(ADXL345_ADDRESS, ADXL_REGISTER_PWRCTL, 1, &data);
  Serial.println((unsigned int)data);
 }
  int accelerometer_data[3];
void read_adxl345() {
 byte bytes[6];
 memset(bytes,0,6);
 //read 6 bytes from the ADXL345
 i2c_read(ADXL345_ADDRESS, ADXL345_REGISTER_XLSB, 6, bytes);
 //now unpack the bytes
 for (int i=0;i<3;++i) {
 accelerometer_data[i] = (int)bytes[2*i] + (((int)bytes[2*i + 1]) << 8);
 }
}
#define ITG3200_ADDRESS (0xD0 >> 1)
//request burst of 6 bytes from this address
#define ITG3200_REGISTER_XMSB (0x1D)
#define ITG3200_REGISTER_DLPF_FS (0x16)
#define ITG3200_FULLSCALE (0x03 << 3)
#define ITG3200_42HZ (0x03)
void init_itg3200() {
  byte data = 0;
  //Set DLPF to 42 Hz (change it if you want) and
  //set the scale to "Full Scale"
  i2c_write(ITG3200_ADDRESS, ITG3200_REGISTER_DLPF_FS, ITG3200_FULLSCALE | ITG3200_42HZ);
  //Sanity check! Make sure the register value is correct.
  i2c_read(ITG3200_ADDRESS, ITG3200_REGISTER_DLPF_FS, 1, &data);
  Serial.println((unsigned int)data);
}
int gyro_data[3];
void read_itg3200() {
  byte bytes[6];
  memset(bytes,0,6);
  //read 6 bytes from the ITG3200
  i2c_read(ITG3200_ADDRESS, ITG3200_REGISTER_XMSB, 6, bytes);  //now unpack the bytes
  for (int i=0;i<3;++i) {
  gyro_data[i] = (int)bytes[2*i + 1] + (((int)bytes[2*i]) << 8);
  }
}
#define HMC5843_ADDRESS (0x3C >> 1)
//First data address of 6 is XMSB.  Also need to set a configuration register for
//continuous measurement
 #define HMC5843_REGISTER_XMSB (0x03)
 #define HMC5843_REGISTER_MEASMODE (0x02)
 #define HMC5843_MEASMODE_CONT (0x00)
 void init_hmc5843() {
  byte data = 0;
  //set up continuous measurement
  i2c_write(HMC5843_ADDRESS, HMC5843_REGISTER_MEASMODE, HMC5843_MEASMODE_CONT);
  //Sanity check, make sure the register value is correct.
  i2c_read(HMC5843_ADDRESS, HMC5843_REGISTER_MEASMODE, 1, &data);
  Serial.println((unsigned int)data);
}
int magnetometer_data[3];
void read_hmc5843() {
 byte bytes[6];
 memset(bytes,0,6);
  //read 6 bytes from the HMC5843
 i2c_read(HMC5843_ADDRESS, HMC5843_REGISTER_XMSB, 6, bytes);
  //now unpack the bytes
 for (int i=0;i<3;++i) {
 magnetometer_data[i] = (int)bytes[2*i + 1] + (((int)bytes[2*i]) << 8);
 }
}
 void setup() {
  Wire.begin();
  Serial.begin(9600);
  for(int i = 0; i < 3; ++i) {
    accelerometer_data[i] = magnetometer_data[i] = gyro_data[i] = 0;
  }
  init_adxl345();
  init_hmc5843();
  init_itg3200();
}
 void loop() {
   read_adxl345();
   read_hmc5843();
   read_itg3200();
   
   Serial.print(accelerometer_data[0]);
   Serial.print(',');
   Serial.print(accelerometer_data[1]);
   Serial.print(',');
   Serial.print(accelerometer_data[2]);
   Serial.print(',');
   Serial.print(magnetometer_data[0]);
   Serial.print(',');
   Serial.print(magnetometer_data[1]);
   Serial.print(',');
   Serial.print(magnetometer_data[2]);
   Serial.print(',');
   Serial.print(gyro_data[0]);
   Serial.print(',');
   Serial.print(gyro_data[1]);
   Serial.print(',');
   Serial.print(gyro_data[2]);
   Serial.print('\n');
   //Sample at 10Hz
   delay(1000); 
}
 
//Processing:
import processing.serial.*;

Serial myPort;  // Create object from Serial class
String val; // Data received from the serial port
float [] accel = new float[3];
float [] gravi = new float[3];
float [] magne = new float[3];
float array[] = new float[9];
float previous[] = new float[9];
String list[];
ArrayList<Ripple> ripples = new ArrayList<Ripple>();
ArrayList<Rain> raining = new ArrayList<Rain>();
int posx;
int posy;
int posz;
int counter = 0;
int counter2 = 0;
int pulse = 1;
int pulse2 =1;
int random = 0;
void setup(){
  // 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()[2]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this, portName, 9600); 
fullScreen();
ellipseMode(CENTER);
}
void draw()
{
  background(255, 255, 255, 21);
  translate(width/2, height/2);
  
  serialEvent(myPort);
  
  formatStringToFloat();
  
  seperateData();
  
  readData();
  
  drawWaterEffects();
}
void serialEvent(Serial myPort)
{
 if ( myPort.available() > 0) 
  {  // If data is available,
  val = myPort.readStringUntil('\n');// read it and store it in val
  if (val != null && val.length() > 1){
    println(val);
    list = split(val, ',');
    for (int i=0; i<list.length; i++){
      println(list[i]);
    }
  } 
 }
}
void formatStringToFloat(){
    if (list != null){
      if (array != null){
        for (int l = 0; l<array.length; l++){
        previous[l] = array[l];
        println(previous[l]);
      }
      }
      for (int i = 0; i<list.length; i++){
        array[i] = Float.valueOf(list[i]);
        println(array[i]);
      }
    }
}
void seperateData(){
  if (array != null){
    for (int i = 0; i<3; i++){
      accel[i] = array[i];
    }
    for (int j = 3; j<6; j++){
      gravi[j-3] = array[j];
    }
    for (int k = 6; k<9; k++){
      magne[k-6] = array[k];
    }
    //println(accel);
    //println(gravi);
    //println(magne);
  }
}
void readData(){
  if (accel[0] > 100){
    posx = -600;
  }
  if (accel[0] < 100 && accel[0] > -100){
    posx = 0;
  }
  if (accel[0] < -100){
    posx = 600;
  }
  if (accel[1] > 100){
    posy = -300;
  }
  if (accel[1] < -100){
    posy = 300;
  }
  if (accel[1] < 100 && accel[1] > -100){
    posy = 0;
  }
  
  float rotate = previous[4] - gravi[1];
  if (rotate > 100 || rotate < -100){
    random = 1;
  }
  
  if (rotate < 100 && rotate > -100){
    random = 0;
  }
  
  float rotateZ = previous[5] - gravi[2];
  if (rotateZ > 100 || rotateZ < -100){
    random = 1;
  }
  
  if (rotateZ < 100 && rotateZ > -100){
    random = 0;
  }
    
}
void drawWaterEffects(){
  if (random == 0){
  if (pulse == 1){
    Ripple r = new Ripple();
    ripples.add(r);
    counter +=1;
    if (counter == 5){
      pulse = 0;
    }
  }
  
  if (pulse == 0){
    counter -=1;
    if (counter == 0){
      pulse = 1;
    }
  }
  
  for (int i=0; i < ripples.size(); i++){
    ripples.get(i).update();
    ripples.get(i).display();
   
    if(ripples.get(i).fade()) {
      ripples.remove(i);
    }
  }
  }
  
  if (random == 1){
    if (pulse2 == 1){
      Rain a = new Rain();
      raining.add(a);
      counter2 +=1;
      if (counter2 == 5){
        pulse2 =0;
      }
    }
    if (pulse2 == 0){
      counter2 -=1;
      if (counter2 == 0){
        pulse2 = 1;
      }
    }
  }
  
  for (int i=0; i < raining.size(); i++){
    raining.get(i).update();
    raining.get(i).display();
   
    if(raining.get(i).fade()) {
      raining.remove(i);
    }
  }
  
}
class Ripple{
  PVector loc;
  int distance;
  color strokeColor;
  
  Ripple(){
    loc = new PVector();
    
    loc.x = posx;
    loc.y = posy;
    
    distance = 1;
    
    strokeColor = color(200, 200, 255);
  }
  
  void update(){
    distance +=2;
  }
  
  void display(){
    stroke(strokeColor);
    
    ellipse(loc.x, loc.y, distance, distance);
  }
  
  boolean fade(){
  if (distance > 300){
    return true;
  }
    return false;
  }
}
class Rain{
  PVector loc;
  int distance;
  color strokeColor;
  
  Rain(){
    loc = new PVector();
    
    loc.x = random(-400, 400);
    loc.y = random(-400, 400);
    
    distance = 1;
    
    strokeColor = color(0, 0, 255);
  }
  
  void update(){
    distance +=2;
  }
  
  void display(){
    stroke(strokeColor);
    
    ellipse(loc.x, loc.y, distance, distance);
  }
  
  boolean fade(){
  if (distance > 300){
    return true;
  }
    return false;
  }
}
IAT320 - Advance Tangible Object
Published:

IAT320 - Advance Tangible Object

Ripple in the Rain, an advance tangible object designed for assignment 3 of IAT320.

Published: