Documentation – 62-362 Fall 2021 https://courses.ideate.cmu.edu/62-362/f2021 Electronic Logics && Creative Practice Sat, 13 Nov 2021 00:54:23 +0000 en-US hourly 1 https://wordpress.org/?v=5.7.11 Project No. 2: Success in Their Eyes https://courses.ideate.cmu.edu/62-362/f2021/project-no-2-success-in-their-eyes/ Sat, 13 Nov 2021 00:54:23 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11749

Leah’s hands positioned over color sensor; the box glows green

The four vases made out of plastic and painted matte black and gold

A scan is complete and the waterfall starts

The initial amount of water in each vase; the pump can be seen in the fifth vase

This white box contains the color sensor, the neopixels, and the switch to activate the sensor

This black box located underneath the pillar holds the Arduino and wiring for the pump

Description

There is a small white box with a black switch on a white pillar. The small white box is connected to a black structure with 5 black and gold vases each containing water. The white box glows blue until the user flips the switch. Once the switch is flipped, the white box will flash green and give the user some time to position their hands. The user holds their hand over the white box and waits for the color scan. The waterfall will start running and lighter skin tones will cause the waterfall to run longer. When the water stops flowing, the box will glow blue again.

Project Reflection

First time wiring up the color sensor

The completed base of the waterfall made of styrofoam

Failed attempt of making the waterfall vases with concrete

Plastic vases are chosen as a substitute for the concrete ones

The waterfall base is coated in the first layer of concrete (works better than the original vases!!)

The vases are painted black with a dash of gold, next step is to paint the base

 

Success In Their Eyes is a project that challenges us to think about how colorism impacts our society and many others throughout the world. The idea developed from the past Jim Crow laws, segregated water fountains, as well as the use of skin lightening cream in several cultures. Throughout the creation, I faced several challenges, not only in the physical design but also in the way I wanted it to be perceived. I completed the technological part very early in the process, though calculating the lightness of color was a struggle. The examples from the Adafruit TCS library helped a lot and with some tweaking of the gain, it was functional! I was able to present the pump and scanner for our first checkpoint.

The biggest hurdle was creating the physical waterfall. I hadn’t given much thought to how the waterfall should look which was a mistake in the process. I finally settled on a Youtube video as my tutorial. I also watched other videos that used concrete to construct the vases, and I was inspired to try. Unfortunately, the process was problematic; the concrete mix was the wrong type and consistency which caused the vases not to dry. The backup plan was to use plastic mason jars and restructure them to the style I wanted. Next time I attempt working with concrete, I wouldn’t rush the research so I end up with something useable. The base was made out of styrofoam and creating it was fairly simple. The complication here was the tube. It wasn’t flexible enough to curve and fit in the base but a simple replacement solved that problem.

The pump and vases all were water tested and worked well individually. After putting them all together for the final product and adding water, I found out there was a major leak in the first and last vase. This was extremely disappointing since I spent so long creating the waterfall piece by piece. In the future, I would like to find a way to patch the problem areas, but recreating the whole waterfall might be easier.

The last struggle was thinking of how users would experience this project. Originally, I wanted the users (most likely in a group) to play around with it and discuss why some people saw the waterfall flow longer. They would come to the realization that one of the visible differences in the group was the color of their skin, and the role it plays in how people are viewed. Additionally, I did not solve how to get users to present their hands without the color scanner being obvious. Given additional time, I would test the completed version with others and take into account the thoughts they have. While it would have been ideal to finish without issues, I loved every moment of fabricating this project and seeing it come to life.

Code

// Judenique Auguste
// Success in Their Eyes
// Creates an instances of the Adafruit Neopixels and Adafruit Color Sensor
// The Neopixels are used to give the status of the waterfall through color
// The Color Sensor is used to caluculate the brightness of the user's skin
// A digital signal is sent to the pump which turns it on and off for a period of time

// Use of Neopixel library example code and Color Sensor library example code

#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <Adafruit_NeoPixel.h>

#define pump 4
#define button 7
#define PIN 6
#define NUMPIXELS 8
#define BRIGHTNESS 50
int pumpState = LOW;
int buttonState;

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_614MS , TCS34725_GAIN_16X);

void setup() {
  Serial.begin(9600);
  pinMode(pump, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  pixels.begin();
  buttonState = digitalRead(button);


  if (tcs.begin()) {
    //Serial.println("Found sensor");
    tcs.setInterrupt(true);
    pixels.begin();
    pixels.setBrightness(BRIGHTNESS);
    buttonState = digitalRead(button);
  } else {
    Serial.println("No TCS34725 found ... check your connections");
    while (1);
  }
}

void loop() {
  // fountain is idle
  for (int i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 255));
    pixels.show();
  }

  float red, green, blue;

  if (buttonState != digitalRead(button)) {
    buttonState = digitalRead(button);
    pixels.clear();
    // the wait period til scan to position
    int position_time = 0;
    while (position_time < 3) {
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, pixels.Color(0, 255, 0));
        pixels.show();
      }
      delay(1000);

      for (int i = 0; i < NUMPIXELS; i++){
        pixels.setPixelColor(i, pixels.Color(0, 0, 0));
        pixels.show();
      }
      delay(1000);
      position_time++;
    }
    tcs.setInterrupt(false);
    delay(200);
    tcs.getRGB(&red, &green, &blue);
    int colorTemp = tcs.calculateColorTemperature(red, green, blue);
    int lux = tcs.calculateLux(red, green, blue);

    tcs.setInterrupt(true);
    int lightness = 765 - (int(red) + int(green) + int(blue));
    int brightness = map(lightness, 650, 0, 0, 255);
    int seconds_on = map(brightness, 0, 255, 6000, 25000);
    int new_time = seconds_on + 5000;

    Serial.print("bright:\t"); Serial.print(brightness);
    Serial.print("\tR:\t"); Serial.print(int(red));
    Serial.print("\tG:\t"); Serial.print(int(green));
    Serial.print("\tB:\t"); Serial.print(int(blue));
    Serial.print("\n");
    Serial.print("length of time:\t"); Serial.print(new_time);
    Serial.print("\n");
    Serial.print("length of time2:\t"); Serial.print(seconds_on);
    Serial.println("\n");

    pumpState = HIGH;
    digitalWrite(pump, pumpState);
    
    //length of time the pump will be on
    while((seconds_on*seconds_on) > 0) {
      rainbow(20);
      seconds_on -= 5000;
    }

    pumpState = LOW;
    digitalWrite(pump, pumpState);
    delay(10);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for (j = 0; j < 256; j++) {
    for (i = 0; i < pixels.numPixels(); i ++) {
      pixels.setPixelColor(i, Wheel((i + j) & 255));
    }
    pixels.show();
    delay(wait);
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

Online Resources

MakerCase – Easy Laser Cut Case Design

Overview | Adafruit Color Sensors | Adafruit Learning System

Waterfall guide | Youtube

]]>
project 2: Thoughts of a Plant https://courses.ideate.cmu.edu/62-362/f2021/project-2-thoughts-of-a-plant/ Sat, 13 Nov 2021 00:36:13 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11849 Thoughts of a Plant explores the differences and similarities between plants and people by setting up an opportunity to listen to and interact with the internal electrical signals of a plant.

Normally, plants exist in the background and slowly grow over time, responding primarily to changes in sunlight, moisture, temperature, or wounding. Humans on the other hand respond quickly to changes around them and constantly communicate through speech to get food, socialize, and generally enact agency. This project is intended to give an interactive voice to plants.

The user listens to an ambient acoustic chord whose timbre changes and evolves with the plants electrical biosignals. A microphone provides the opportunity to interact with the biosignals by inducing a complementary signal into the plant based on your speech. This allows the user to create fast-changing effects on the timbre and see how their own speech is transduced through the plant amplification circuit. The intended effect is to contrast our short-term speech patterns against the slower changes of the plant’s own signals.

The plant is set up nondescriptly on a table with visible electrical probes and amplification circuitry. A prominent microphone invites visitors to speak into the plant and listen to the reply.

 

Process

Thoughts of a Plant uses electrical probes attached to the stems of the plant to measure small voltages across the leaves. The signals are sent through an arduino equipped with a custom amplifier circuit to a software synthesizer and modulate parameters such as filter cutoffs, resonance, noise, detuning, overdrive, and a few other parameters. This means that slight changes in the plant’s bioelectrical signals modify the timbre of the ambient tone. The chord itself is a Minor 7th played from through a dual-sawtooth synthesizer to produce a somewhat dark, warm ambient tone. Additional instruments provide relatively static harmonics to build out a larger acoustic atmosphere.

The primary challenge here was actually measuring the plant’s bioelectrical signals. Most of the work for this project went into designing and constructing a very high input-impedence amplifier/filter circuit so that I could measure the small signals on the plant. I ended up using a noninverting op-amp circuit with a first order low-pass filter, with a gain of 10 and cutoff frequency of 100Hz respectively. A virtual ground allows the measurement of negative signals by biasing the output signal to 2.5v. This turned out to be very difficult to implement in practice, and took most of my time. The hardest part is that plant signals evolve very slowly (noticeable change takes a couple minutes, and significant change takes 10-20 minutes), so it takes a long time to verify that the system is working and measuring plant data rather than arbitrary noise.

The synthesis was fairly simple, but interfacing with the arduino was surprisingly buggy and difficult using Max For Live. Max would often cause ableton to freeze and mapping signals (ie, arduino A0 to filter cutoff) was unreliable and lacked customization.

I think I ended up with a good probing setup, but I wish I had used custom sound synthesis instead of Max For Live. I ended up with very little control over the timbre changes, so some effects were far more sensitive than others. The microphone input in practice was fairly rough, since Max For Live’s envelope follower module was very buggy and unreliable, so I couldn’t tweak it easily. I wish I had processed the user audio in a more interesting way, such as with an fft or slowing down the response. I also think that it was generally unclear what role the microphone played in the setup, so a little bit of signage and better introduction could have helped there.

Final Images

Audio of the plant synthesizer in action:

Process Images:

The measured signal evolves slowly over 10-20 minutes:

A video of the synthesizer modulation in ableton using Max For Live:

 

Code:

There was no original “code” for this project. A simple ableton synthesizer was run and arduino code was just the Max For Live client sketch.

]]>
Apply Pressure Flow Documentation – Olivia https://courses.ideate.cmu.edu/62-362/f2021/apply-pressure-flow-documentation-olivia/ Fri, 12 Nov 2021 21:57:53 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11704 Apply Pressure

Final Design of the Touch Pad

Video Demonstration

 

Examples of the Visuals

Narrative description

Apply pressure is an installation that produces visuals and sounds. The physical part of the project is a wooden box, covered in shiny green fabric, with nine force sensors on it that act as buttons. When each button is pressed, colored circles are projected onto a screen and a sound is produced. As the buttons are pressed harder, the nine circles which are projected in a grid on the screen get larger and the sound plays louder.

Progress Images

This photo shows my first prototype where I struggled to find a rigid material to cover the sensors with. The pressure was not transferring to the sensors even through the thinnest sheet material so I decided to just cover the exposed part of the sensors with fabric in the final design. The fabric I chose did not interfere with the touch sensitivity of the resistors.

This photo was taken the day I finally got the serial connection to work between processing and the arduino. I was starting to get the visuals I wanted and experimenting with different fabrics to put over top of the touch pad.

This photo shows me building the final box that would house the touchpad on the top and the breadboard and arduino underneath.  I made the final design about twice the size of the prototype so that it was large enough for multiple people to interact with it at once. The hole at the center of the box was where all of the wires from the resistors in the touchpad would come together.

This photo shows the bottom side of the box where the majority of the electronics would be housed on the wooden piece that bridges the length of the box. This was so that the box  was mobile while the electronics were in place but I could still easily access them.

Process Reflection

This project was my first time using Processing which I used to create the audio-visual aspects of the project. Figuring out how to write code to produce the kind of parametric visuals I wanted was not intuitive for me since I am used to designing in a a more visual programming language like Grasshopper. However, after many experimentations and learning through looking at examples I was able to get the visuals to a point where I was happy. I needed some help working through a few bugs with the serial connection to the arduino but once that was set up, adding in the sound component was pretty simple.  I feel more comfortable using processing now and definitely want to use it to design visuals for my personal website.

Another challenge I had was deciding how to stabilize the sensors in a way that didn’t limit their sensitivity as seen in the process images above. Designing the box wound up being a bit difficult but I figured out I could use two layers of acrylic with misaligned grids of holes to essentially sandwich the sensors and keep them in place. One of the most fun parts of this project was sifting through online music samples and choosing the audio components. I put a lot of thought into how the audios sounded together if multiple people were going to be playing with the buttons at the same time.

If I wanted to push the project in a different direction I could have created a different kind of tactile experience instead of the large touchpad embedded into the box. We had previously talked about possibly building nine separate remotes so that the visuals could be collectively produced by people sitting away from one another. This would have created a more experimental nature in the way the project is experienced since it wouldn’t be as obvious which sensor controlled which part of the visuals and audio. In this case the users would have to figure out who controlled what which could have been fun even though I did enjoy the close collaboration that this project required. Overall I am happy with the way this project came out and it seemed like people were excited about interacting with it during the review.

Code submission

Arduino Code:

//Apply Pressure
//By Olivia Werner
//This code reads analog input data from nine force sensitive
//resistors and maps the values to be in a range of 0 - 255

const int FCR0 = A0; //defining analog sensors
const int FCR1 = A1;
const int FCR2 = A2;
const int FCR3 = A3;
const int FCR4 = A4;
const int FCR5 = A5;
const int FCR6 = A6;
const int FCR7 = A7;
const int FCR8 = A8;

void setup() {
  // put your setup code here, to run once:
  pinMode(FCR0, INPUT);
  pinMode(FCR1, INPUT);
  pinMode(FCR2, INPUT);
  pinMode(FCR3, INPUT);
  pinMode(FCR4, INPUT);
  pinMode(FCR5, INPUT);
  pinMode(FCR6, INPUT);
  pinMode(FCR7, INPUT);
  pinMode(FCR8, INPUT);
  Serial.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  //if we get a valid byte, read analog ins:
  //if (Serial.available() > 0) {
  // get incoming byte:
  //inByte = Serial.read();

  //read the value of the resistance and store it
  int forceVal0 = analogRead(FCR0);
  //map value to 255
  forceVal0 = map(forceVal0, 0, 1023, 0, 255);
  Serial.print(forceVal0);
  Serial.print(",");
  delay(10);

  int forceVal1 = analogRead(FCR1);
  //map value to 255
  forceVal1 = map(forceVal1, 0, 1023, 0, 255);
  Serial.print(forceVal1);
  Serial.print(",");
  delay(10);

  int forceVal2 = analogRead(FCR2);
  //map value to 255
  forceVal2 = map(forceVal2, 0, 1023, 0, 255);
  Serial.print(forceVal2);
  Serial.print(",");
  delay(10);

  int forceVal3 = analogRead(FCR3);
  //map value to 255
  forceVal3 = map(forceVal3, 0, 1023, 0, 255);
  Serial.print(forceVal3);
  Serial.print(",");
  delay(10);

  int forceVal4 = analogRead(FCR4);
  //map value to 255
  forceVal4 = map(forceVal4, 0, 1023, 0, 255);
  Serial.print(forceVal4);
  Serial.print(",");
  delay(10);

  int forceVal5 = analogRead(FCR5);
  //map value to 255
  forceVal5 = map(forceVal5, 0, 1023, 0, 255);
  Serial.print(forceVal5);
  Serial.print(",");
  delay(10);

  int forceVal6 = analogRead(FCR6);
  //map value to 255
  forceVal6 = map(forceVal6, 0, 1023, 0, 255);
  Serial.print(forceVal6);
  Serial.print(",");
  delay(10);

  int forceVal7 = analogRead(FCR7);
  //map value to 255
  forceVal7 = map(forceVal7, 0, 1023, 0, 255);
  Serial.print(forceVal7);
  Serial.print(",");
  delay(10);

  int forceVal8 = analogRead(FCR8);
  //map value to 255
  forceVal8 = map(forceVal8, 0, 1023, 0, 255);
  Serial.print(forceVal8);
  Serial.print(",");

  Serial.println("0"); //FAKE VALUE TO DEBUG

  delay(10);
}

Processing Code:

//Apply Pressure
//By Olivia Werner
//This code reads serial data from nine force sensitive resistors connected to an arduino mega
//Nine circles are drawn with their size being constantly updated depending on the amount of pressure on the 
//force sensitive resistors. The hue of the circles updates randomly creating a gradient. The code also
//plays nine sound files depending on which sensor is being pressed with the volumes controlled by the analog values.


import processing.serial.*;
import cc.arduino.*;
import org.firmata.*;
import processing.sound.*;

SoundFile[] file;
// Define the number of samples
int numSounds = 10;

Arduino arduino;

int timer;

Serial myPort; //create an object
String val; //data recieved from the serial port

int[] serialInArray = new int[10]; // Where we'll put what we receive
int serialCount = 0;     // A count of how many bytes we receive
String inString;

ArrayList<Circle> circles; //make an array of circles

void setup() {

  size(1500, 1500);
  colorMode(HSB, 100);
  smooth();
  noFill();
  ellipseMode(CENTER);

  // Create an array of empty soundfiles
  file = new SoundFile[numSounds];

  // Load 9 soundfiles from a folder in a for loop. By naming the files 0., 1., 2., n.aif it is easy to iterate
  // through the folder and load all files in one line of code.
  for (int i = 0; i < numSounds; i++) {
    file[i] = new SoundFile(this, (i+1) + ".aif");
  }

  //create an array of circles
  circles = new ArrayList<Circle>();

  // set the background color with the color values:
  background(serialInArray[7], serialInArray[6], serialInArray[3]);

  // Open whatever port is the one you're using.
  String portName = "COM5";
  myPort = new Serial(this, "COM5", 115200);
}

void draw() {

  for (Circle  c : circles) {
    c.draw();
    c.update();
  }
  //stop storing circle data after there are 150 circles
  while (circles.size()>150) {
  circles.remove(0);
  }

  if (millis() - timer > 200)
  {
    //draw nine circles
    circles.add(new Circle(375, 375, serialInArray[0]*3, serialInArray[0]*3));
    circles.add(new Circle(750, 375, serialInArray[1]*4, serialInArray[1]*4));
    circles.add(new Circle(1125, 375, serialInArray[2]*8, serialInArray[2]*8));

    circles.add(new Circle(375, 750, serialInArray[3]*5, serialInArray[3]*5));
    circles.add(new Circle(750, 750, serialInArray[4]*8, serialInArray[4]*8));
    circles.add(new Circle(1125, 750, serialInArray[5]*6, serialInArray[5]*6));

    circles.add(new Circle(375, 1125, serialInArray[6]*6, serialInArray[6]*6));
    circles.add(new Circle(750, 1125, serialInArray[7]*3, serialInArray[7]*3));
    circles.add(new Circle(1125, 1125, serialInArray[8]*4, serialInArray[8]*4));

    timer = millis();
  }

  //play sounds
  for (int i = 0; i< numSounds-1; i++) {
    if (!file[i].isPlaying() && serialInArray[i] >0) {
      float volumeLevel = map(serialInArray[i], 0, 255, 0, 1.0); //volume
      file[i].play(1, volumeLevel);
    }
  }
}

class Circle {
  int hue = 0;
  float x, y, w, h, sw;

  Circle(float x, float y, float w, float h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    sw = random(2, 2);
  };
  
//defining properties of the circles
  void draw() {
    strokeWeight(sw);
    stroke(hue, 100, 100, 100);
    fill(hue, 100, 100, 25);
    ellipse(x, y, w, h);
  }
  
//update gradient
  void update() {
    hue++;
    if (hue == 100) {
      hue = 0;
    }
  }
}

void serialEvent(Serial p) {
  inString = p.readStringUntil('\n');

  if (inString != null) {
    //println(inString);
    
    serialInArray = int(split(inString, ','));
    printArray(serialInArray);
  }
}
]]>
Project No. 2: Universe Comparator https://courses.ideate.cmu.edu/62-362/f2021/project-no-2-universe-comparator/ Fri, 12 Nov 2021 21:26:32 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11724 Description

The Universe Comparator is an interactive project where people try to understand the scale of objects all around the universe relative to each other. If you ever wondered how many humans will fit into the Milky Way or how many ants can span across the Grand Canyon, this is the project you want.

There is a big balloon whose size represents 1 of an object shown on the screen. There is also a number you control on screen. Together, you can find out what these number of objects combine to be equivalent to. For example, if you set the number to 10 and object to Jupiter, you will find that 10 Jupiters are roughly equal to 1 Sun in diameter. You can control this number and object by tilting your hand forward, backward, left and right. There is a sensor (accelerometer) which detects these movements and connects it to certain actions. Tilting it forward/backward means the balloon will inflate/deflate and the object will change. Tilting it left and right will increase/decrease the number.

The balloon and sensors were controlled by an Arduino, pneumatics and solenoid valves connected to atmosphere and air compressor tank. The calculations and display was done through Processing.

In-Progress Media and Code

Planning and buying the pneumatic components for airflow

Initial circuitry to test the sensors and relays

Final Test Run before the Due Date

// Laser Time of Flight sensor (VL52L0X)

#include "Adafruit_VL53L0X.h"

Adafruit_VL53L0X lox = Adafruit_VL53L0X();

void setup() {
  Serial.begin(115200);

  // wait until serial port opens for native USB devices
  while (! Serial) {
    delay(1);
  }
  
  Serial.println("Adafruit VL53L0X test");
  if (!lox.begin()) {
    Serial.println(F("Failed to boot VL53L0X"));
    while(1);
  }
  // power 
  Serial.println(F("VL53L0X API Simple Ranging example\n\n")); 
}


void loop() {
  VL53L0X_RangingMeasurementData_t measure;
    
  Serial.print("Reading a measurement... ");
  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!

  if (measure.RangeStatus != 4) {  // phase failures have incorrect data
    Serial.print("Distance (mm): "); Serial.println(measure.RangeMilliMeter);
  } else {
    Serial.println(" out of range ");
  }
    
  delay(100);
}
// This program was used to help control the accelerometer
/*
 * This program reads an accelerometer connected to ACCEL_X_PIN,
 * ACCEL_Y_PIN, and ACCEL_Z_PIN and sends the data back to the
 * computer via serial.
 *
 * Created 2021-02-19 by Perry Naseck
 */

const int ACCEL_X_PIN = A0; // Analog input pin for X movement
const int ACCEL_Y_PIN = A1; // Analog input pin for Y movement
const int ACCEL_Z_PIN = A2; // Analog input pin for Z movement

// Variables to keep track of the current positions
int accelXPos = 0;
int accelYPos = 0;
int accelZPos = 0;

void setup() {
  // Setup serial port to send the accelerometer data back to the computer
  Serial.begin(115200); 
  
  // Setup the accelerometer inputs
  pinMode(ACCEL_X_PIN, INPUT);
  pinMode(ACCEL_Y_PIN, INPUT);
  pinMode(ACCEL_Z_PIN, INPUT);
}

void loop() {
  // Get the current states
  accelXPos = analogRead(ACCEL_X_PIN);
  accelYPos = analogRead(ACCEL_Y_PIN);
  accelZPos = digitalRead(ACCEL_Z_PIN);
  
  // Send the data over serial
  Serial.print("X: ");
  Serial.print(accelXPos);
  Serial.print(" Y: ");
  Serial.print(accelYPos);
  Serial.print(" Z: ");
  Serial.println(accelZPos);

  // Delay to not send messages too fast
  delay(100);
}

Process Reflection

What worked well?

Thanks to the continued effort from Professor Zach and I, the system with the balloon functioned well. There were no leaks in air. The pressure was good. The sensor inside the balloon was responsive. The relays worked well with the solenoid valves and responded to the accelerometer instantly. Fastening the setup to a board helped make it look cleaner and easier to carry.

While the display was not the most effective, the programming behind it was still something that worked out well. I worked on sub-modules with smaller goals like recognizing an object, parsing data, etc. and combined them in the end. There was a lot of complexity behind the scenes (which I will talk about in the next section), which is why I was a bit worried that entire module won’t click together at the end. However, it turned out to respond and communicate with the Arduino well and without errors by the final demo.

Lastly, I enjoyed mounting the sensor on the Velcro through knitting. I’d like to thank Professor Mallea who helped me with doing so. The sensor looked clean and was relatively easy to wear.

Challenges faced

Since it was my first time working with Processing and making it communicate to Arduino, I ran into many issues. Fortunately, having some coding knowledge from my ECE background helped pinpoint and debug errors more easily than I would otherwise. Some of the common issues I ran into that I spent a while figuring out were:-

  • Parsing serial data from Arduino and handling cases where it would sometimes send ‘null’ to crash the program
  • Realizing that strings can’t be compared with == sign
  • Data types having maximum and minimum ceilings and working around that
  • Indexing into weird lists/dictionaries when data wasn’t communicated properly from Arduino

I also spent a good amount of time coding the calculation and finding the equivalent object it corresponded to. Dictionaries and lists were my friends in this case.

Finally, a lot of time was also spent to figure out the connections between the air compressor tank and balloons. Through the help of Professor Zach, who had a little bit of experience with this, we were able to find the right components to make this system work.

What I could have done better?

The balloon and the screen felt very disconnected in the end product. A larger display closer to the balloon would have strengthened their connection better. Furthermore, while the written information conveyed the difference in scale, this effect is nearly not as effective as showing a visual comparison on the screen as well. Dealing with the many issues in programming, I did not have enough time to implement a visual scaling comparison on the display.

Similar to the last project, the wires were out on display again. Some sort of a covering or container to hide everything but the balloons and the screen would have definitely made it more aesthetically pleasing. Last but not least, my initial plan was to make people feel more insignificant in the huge scale of things once they interacted with this project. I don’t believe I achieved that. To do so, I feel that I need to build more of an atmosphere or a story and a constant reference of their own size to the everything else.

Lastly, the demo and suggestions showed me that the project could have gone in an entirely different direction based on how the balloon turned out. The clicking noises of the relays and valves with the loud compressor tank created an atmosphere of being alert. The balloon growing larger and larger induced a sense of danger. Working around these aspects, I might have been able to make a more ‘Halloween’ themed project to create a scary atmosphere.

Learning

This has definitely been one of the few projects throughout my life that taught me so many different things. Processing is a new tool I learnt, which seems to have so many more cool things to explore. Working with pneumatics, compressor tanks and air is another big thing I learnt. Basic knitting is also something I got to experience and learn. My arsenal of new tools expanded by a lot, and I am grateful for this experience.

Universe Comparator in Action

Content on the Display Screen

Participant inflating the balloon and observing the display

Participant wearing the 3-axis accelerometer

Inflation of the balloon to a decently large size

Participant changing the number on the display

Video of Universe Comparator (File too large/poor quality after compression to fit here):

https://drive.google.com/file/d/1YU0wM-NZFxYr8IlpXU9Y9c5c38fPb6R9/view?usp=sharing

Same video on Youtube:

Code

/*
   Universe Comparator: Arduino Code, Tushhar Saha
   This sketch uses the accelerometer to control relays attached to
   the balloon inflation and deflation mechanism. Furthermore, it
   translates data from VL53L0X and accelerometer to send to 
   Processing sketch in Serial.
*/
#include "Adafruit_VL53L0X.h"
const int ACCEL_X_PIN = A0; // Analog input pin for X movement
const int ACCEL_Y_PIN = A1; // Analog input pin for Y movement
const int ACCEL_Z_PIN = A2; // Analog input pin for Z movement
const int Balloon_inf = 12;
const int Balloon_def = 11;
const int offset = -100;

// Variables to keep track of the current positions
int accelXPos = 0;
int accelYPos = 0;
int accelZPos = 0;
int num = 1;

Adafruit_VL53L0X lox = Adafruit_VL53L0X();

void setup() {
  Serial.begin(115200);

  // wait until serial port opens for native USB devices
  while (! Serial) {
    delay(1);
  }

  if (!lox.begin()) {
    Serial.println(F("Failed to boot VL53L0X"));
    while (1);
  }

  pinMode(ACCEL_X_PIN, INPUT);
  pinMode(ACCEL_Y_PIN, INPUT);
  pinMode(ACCEL_Z_PIN, INPUT);
  pinMode(Balloon_inf, OUTPUT);
  pinMode(Balloon_def, OUTPUT);
}

void loop() {
  VL53L0X_RangingMeasurementData_t measure;

  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!

  if (measure.RangeStatus != 4) {  // phase failures have incorrect data
    //Serial.print("Distance (mm): ");
    int distance = measure.RangeMilliMeter;
    String object = whichObject(distance);
    // Send to Processing
    Serial.print(object); //object
  } else {
    Serial.print("Observable Universe"); //out of range
  }

  Serial.print(",");
  accelXPos = analogRead(ACCEL_X_PIN);
  accelYPos = analogRead(ACCEL_Y_PIN);
  num = calculateNumber(num, accelYPos);
  Serial.println(num);

  // Inflation and Delfation
  if (accelXPos < 310) {
    digitalWrite(Balloon_inf, LOW);
    digitalWrite(Balloon_def, HIGH);
  } else if (accelXPos < 360) {
    digitalWrite(Balloon_inf, LOW);
    digitalWrite(Balloon_def, LOW);
  } else { //set max
    digitalWrite(Balloon_inf, HIGH);
    digitalWrite(Balloon_def, LOW);
  }

  delay(500);
}

// Corresponding celestial objects sent to Processing
String whichObject(int dist) {
  // x = 250,290,330,375,400,450,500,550,600,650 (add a max)
  // "Ants", "Humans", "Grand Canyon", "Earths", "Suns", "Diameter of Solar System", "1 Light Year", "The Milky Way", "The Local Group of Galaxies", "Observable Universe"
  if (dist < (offset + 250)) {
    return "Ant";
  } else if (dist < (offset + 290)) {
    return "Human";
  } else if (dist < (offset + 340)) {
    return "Grand Canyon";
  } else if (dist < (offset + 375)) {
    return "Earth";
  } else if (dist < (offset + 425)) {
    return "Sun";
  } else if (dist < (offset + 500)) {
    return "Diameter of Solar System";
  } else if (dist < (offset + 550)) {
    return "1 Light Year";
  } else if (dist < (offset + 600)) {
    return "The Milky Way";
  } else if (dist < (offset + 650)) {
    return "The Local Group of Galaxies";
  } else {
    return "Observable Universe";
  }
}

//Thresholds for number sent to processing
int calculateNumber(int number, int x) {
  if (x < 265) {
    return number + 2;
  } else if (x < 290) {
    return number + 1;
  } else if (x < 350) {
    return number;
  } else if (x < 365) {
    return number - 1;
  } else {
    return number - 2;
  }
}
/*
Universe Comparator: Processing Code, Tushhar Saha
 This sketch receives data from Arduino for the number input and the
 object equivalent to the size of the ballon. This data is shown visually,
 and it is also used to calculate equivalent celestial objects.
 */
import processing.serial.*;

PFont f;
FloatDict univObjects;
float ly;
float[] diameters;
String[] objects;
StringDict findObject;

Serial myPort;  // Create object from Serial class
String val;     // Data received from the serial port
String object;
String number;
PImage[] img_objects;
PImage pic1;
PImage pic2;
PImage Uranus;
PImage Arrow;
PImage DownArrow;
PImage title;

void setup() {
  size(2500, 2000);
  // Setting up a dictionary with sizes as values
  ly = 9.5*pow(10, 12);
  univObjects = new FloatDict();
  univObjects.set("Atom", pow(10, -13));
  univObjects.set("Red Blood Cell", 8*pow(10, -9));
  univObjects.set("Thickness of a Paper", pow(10, -7));
  univObjects.set("Ant", pow(10, -5));
  univObjects.set("Mouse", pow(10, -4));
  univObjects.set("Human", 1.77/1000);
  univObjects.set("Football Field", 0.105);
  univObjects.set("Grand Canyon", 500);
  univObjects.set("Moon", 3500);
  univObjects.set("Earth", 12742);
  univObjects.set("Jupiter", 140000);
  univObjects.set("Sun", 1.39 * pow(10, 6));
  univObjects.set("Distance from the Sun to Earth", 150 * pow(10, 6)); //also equivalent to 1 AU
  univObjects.set("UY Scuti", 24 * pow(10, 9));
  univObjects.set("Diameter of Solar System", 3 * pow(10, 11));
  univObjects.set("TON 618", 4 * pow(10, 11));
  univObjects.set("1 Light Year", ly);
  univObjects.set("Distance between Proxima Centauri and the Sun", 4*ly);
  univObjects.set("Omega Centauri", 160 * ly); //largest globular cluster in Milky Way
  univObjects.set("Sombrero Galaxy", 50000 * ly);
  univObjects.set("The Milky Way", 120000*ly);
  univObjects.set("Tadpole Galaxy", 280000*ly);
  univObjects.set("The Local Group of Galaxies", pow(10, 7)*ly); //contains milky way, etc.
  univObjects.set("Bootes Void", 33*pow(10, 7)*ly); //No galaxies
  univObjects.set("Laniakea Supercluster", 5*pow(10, 8)*ly); //Made up of galaxy groups, supercluster home to Milky way
  univObjects.set("Sloan Great Wall", 1.38*pow(10, 9)*ly); //Giant wall of galaxies (maybe largest structure)
  univObjects.set("Observable Universe", 93*pow(10, 9)*ly);
  univObjects.set("Universe", pow(10, 13)*ly);

  diameters = univObjects.valueArray();
  objects = univObjects.keyArray();
  findObject = new StringDict(); //This dictionary is used to find objects that correspond to a certain size (objects are values here)

  for (int i = 0; i < 28; i = i+1) {
    findObject.set(str(diameters[i]), objects[i]);
  }

  //Images
  img_objects = new PImage[28];
  img_objects[0] = loadImage("Atom.jpg");
  img_objects[1] = loadImage("RBC.jpg");
  img_objects[2] = loadImage("Paper.jpg");
  img_objects[3] = loadImage("Ant.jpg");
  img_objects[4] = loadImage("Mouse.jpg");
  img_objects[5] = loadImage("Human.jpg");
  img_objects[6] = loadImage("Field.jpg");
  img_objects[7] = loadImage("GC.jpg");
  img_objects[8] = loadImage("Moon.jpg");
  img_objects[9] = loadImage("Earth.jpg");
  img_objects[10] = loadImage("Jupiter.jpg");
  img_objects[11] = loadImage("Sun.jpg");
  img_objects[12] = loadImage("DistES.jpg");
  img_objects[13] = loadImage("UYScuti.png");
  img_objects[14] = loadImage("Solar System.jpg");
  img_objects[15] = loadImage("Ton.png");
  img_objects[16] = loadImage("ly.jpg");
  img_objects[17] = loadImage("Proxima.png");
  img_objects[18] = loadImage("Omega.jpg");
  img_objects[19] = loadImage("Somb.jpg");
  img_objects[20] = loadImage("Milky.jpg");
  img_objects[21] = loadImage("Tadpole.jpg");
  img_objects[22] = loadImage("LocalGroup.png");
  img_objects[23] = loadImage("bootes.jpg");
  img_objects[24] = loadImage("Lani.jpg");
  img_objects[25] = loadImage("Sloan.png");
  img_objects[26] = loadImage("ObservableUniverse.png");
  img_objects[27] = loadImage("Uni.jpg");
  Uranus = loadImage("Uranus.jpg");
  Arrow = loadImage("Arrow.jpg");
  DownArrow = loadImage("DownArrow.jpg");
  title = loadImage("title.jpg");
  // Create the font
  // printArray(PFont.list());
  f = createFont("Palatino Linotype Bold", 48);

  textFont(f);

  //Data from Arduino
  String portName = Serial.list()[0]; //change the 0 to a 1 or 2 etc. to match your port
  myPort = new Serial(this, portName, 115200);
}

void draw() {
  background(0);
  textAlign(CENTER);

  //Data from Arduino
  if (myPort.available() > 0)
  { // If data is available,
    val = myPort.readStringUntil('\n'); // read it and store it in val
  }
  if (val==null) {
    return;
  }
  //Parsing Data
  String[] arduinoData = split(val, ',');
  object = arduinoData[0];
  if (arduinoData.length != 2) {
    return;
  }
  number = arduinoData[1];
  number = number.replace('\n', ' ');
  number = trim(number);
  float numI = pow(10, int(number)); // use this
  if (!univObjects.hasKey(object)) { //stops errors in reading
    return;
  }

  float ans = calculateObject(numI, object, univObjects, diameters);
  String answer = findObject.get(str(ans)); //finds equivalent object
  //find images correponding to answers
  pic1 = findImage(object);
  pic2 = findImage(answer);
  //display
  textSize(48);
  imageMode(CENTER);
  image(pic1, 0.25*width, 0.6*height, 0.4 * height, 0.4 * height); //left image
  text("is equivalent to", 0.5*width, 0.6*height);
  text(answer, 0.75*width, 0.9*height);
  drawFact(answer);
  fill(255, 255, 0);
  text("Your balloon size represents 1 of this", 0.25*width, 0.25*height);
  image(DownArrow, 0.25*width, 0.32*height, 0.1 * height, 0.1 * height);
  image(pic2, 0.75*width, 0.6*height, 0.4 * height, 0.4 * height); //right image
  image(Arrow, 0.05*width, 0.6*height, 0.1 * height, 0.4 * height);

  image(title, 0.5*width, 0.1*height, 0.8 * height, 0.175* height);
  textSize(48);
  number = calculateNumber(number);

  drawType(width*0.25, height*0.9, number + " x " + object);
}

// fun facts about objects
void drawFact(String celObj) {
  String fact = "";
  fill(0, 255, 0);
  if (celObj.equals("Sun")) {
    fact = "You can fit 1.3 million Earths in the Sun (volumetrically)";
  } else if (celObj.equals("UY Scuti")) {
    fact = "It is the largest star and can fit 3.69 billion Suns\n (volumetrically)";
  } else if (celObj.equals("Red Blood Cell")) {
    fact = "There are around 25 trillion RBCs in a human body";
  } else if (celObj.equals("Football Field")) {
    fact = "54000 people can fit in a football field";
  } else if (celObj.equals("TON 618")) {
    fact = "It is the largest black hole known to us.\n It is a little larger than our solar system.";
  } else if (celObj.equals("Distance between Proxima Centauri and the Sun")) {
    fact = "It is the closest star to the Sun";
  } else if (celObj.equals("Omega Centauri")) {
    fact = "It is the largest globular cluster of stars in the Milky Way";
  } else if (celObj.equals("Bootes Void")) {
    fact = "It is also referred to as the Great Nothing.\nIt contains almost no galaxies.";
  } else if (celObj.equals("Laniakea Supercluster")) {
    fact = "It is made up of many galaxy groups.\n It is the supercluster home to The Milky Way.";
  } else if (celObj.equals("Sloan Great Wall")) {
    fact = "It is a giant wall of galaxies, and \npossibly the largest structure in the universe.";
  } else if (celObj.equals("Observable Universe")) {
    fact = "The universe is expanding so fast that the light\nfrom beyond this point doesn't reach us.";
  } else if (celObj.equals("Universe")) {
    fact = "Who knows how much further the universe can go?";
  } else if (celObj.equals("Atom")) {
    fact = "A grain of sand has 2x10^19 atoms in it.\nThat is still lesser than the number of stars in the sky.";
  } else if (celObj.equals("Thickness of a Paper")) {
    fact = "Perhaps an ant can get a paper cut?";
  } else if (celObj.equals("Mouse")) {
    fact = "Did you know that mice can fit through a hole\nthe size of a dime?";
  } else if (celObj.equals("Human")) {
    fact = "If the every human was stacked on top of each\nother, we could reach the moon 30 times.";
  } else if (celObj.equals("Grand Canyon")) {
    fact = "You would need around 1200 humans stacked on \ntop of eachother to climb out of the Grand Canyon.";
  }
  text(fact, width*0.75, height*0.35);
}

//Number data to display from Arduino
String calculateNumber(String numb) {
  int no = int(numb);
  String ans = "";
  if ((no==0) || (no==1) || (no==2) || (no==3) || (no==4)) {
    ans = str(int(pow(10, no)));
  } else if (no < 0) {
    float ans1 = pow(10, no);
    ans = str(ans1);
  } else {
    ans = "10^" + numb;
  }
  return ans;
}

void drawType(float x, float y, String val1) {
  fill(255);
  text(val1, x, y);
}

PImage findImage(String obj) {
  for ( int i = 0; i < objects.length; i++ ) {
    if (obj.equals( objects[i] ) ) {
      return img_objects[i];
    }
  }
  return Uranus;
}

// Do math to find the closest object in dictionary to the calculated size
float calculateObject(float numI, String object, FloatDict uni, float[] diam) {
  float num2 = uni.get(object);
  float calcNum = numI * num2;
  int j = -1;
  //Finding nearest value
  for (int i = 0; i < 27; i = i+1) {
    if (calcNum>diam[i]) {
      j = i;
    }
  }

  int finalIndex;
  if (j==-1) {
    finalIndex = 0;
  } else if (j==27) {
    finalIndex = 27;
  } else {
    if ((diam[j+1] - calcNum)<(calcNum-diam[j])) {
      finalIndex = j+1;
    } else {
      finalIndex = j;
    }
  }
  return diam[finalIndex];
}

 

]]>
The Jackpot of Life | Flow | Leah Walko https://courses.ideate.cmu.edu/62-362/f2021/the-jackpot-of-life-flow-leah-walko/ Fri, 12 Nov 2021 18:22:46 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11777 Final Project Pictures and Video

Side image of the slot machine

Front image of the slot machine

Back of it

Holding down the button to spin the wheel

The wheel landing on it’s final position

Waving the magnet around to change where the wheel lands

Computer output based off of the received input values

Narrative Description

The Jackpot of Life is a slot machine, but the user can rig it in their favor. A disproportionate amount of one’s life is determined by the circumstances of their birth, which is the idea behind my project. Each wheel correlates to some aspect of a person’s life, their family’s financial situation, where they are born, and their education level. The user presses a button and the wheel spins to land on some result. As I mentioned, though, it can be rigged. While holding down the button, one can “send their prayers,” which translates to waving a magnet in front of a sensor that measures magnetic fields. Based off of the value read from the magnet, the wheel spins to a specific position.

Progress Images

Wired and programmed the wheel to spin

First prototype. Looks good.

The day before my project is due, I drop my wheel and it breaks

My desperate quick fix solution to set up the three wheels

It works… to an extent.

Process Reflection

I had a lot of problems during this project. I failed to account for a lot of the physical mechanics and innerworkings of the stands that hold up the wheels. To make matters worse, one of my wheels shattered into pieces. I had to use whatever I had to make things work. As a result, it isn’t stable at all and I had to use many unconventional materials to slap things together. The result is that it is kind of a mess. To make matters worse, it would randomly stop working at random intervals and I had to take time to fix it. I definitely didn’t give myself enough time to account for these problems.

In the end, I didn’t have the time to implement a lot of elements to the project that I was looking forward to. I didn’t have time to program and wire a receipt printer to print out the user’s results. I also didn’t have the time to make it visually appealing. I almost wish I didn’t print out any signage at all because it would have looked slightly less slapped together and more intentional if I hadn’t.

In the critique, many points were also brought up that I failed to consider early on in the project. I could’ve given more consideration to the relationship between the input and output and the connection to the overarching idea. My ultimate takeaway is that I should’ve done more detailed consideration and planning early on in the project to know how I would build the stands and box in general. I also should have put more work into the project early on to account for unexpected problems. I believe that I would not have struggled nearly as much if I had built a prototype stand for the prototype.

Code Submission

/*
 * 
 * The Jackpot of Life
 * by Leah Walko
 * 
 * Pin 11 -> left button
 * Pin 12 -> middle button
 * Pin 13 -> right button
 * Pin 2 -> left stepper step
 * Pin 3 -> left stepper direction
 * Pin 4 -> middle stepper step
 * Pin 5 -> middle stepper direction
 * Pin 6 -> right stepper step
 * Pin 7 -> right stepper direction
 * 
 * read a xyz magnetic value ("Prayers") and turn it into 
 * three Stepper value
 * 
 * Sources:
 * QMC5883LCompass.h Library XYZ Example Sketch
 * Learn more at [https://github.com/mprograms/QMC5883Compas]
 * AccelStepper.h library example
 *
 */

 // Pos: 10, 30, 50, 70, 90, 110, 130, 150, 170, 190

#include <QMC5883LCompass.h>
#include <AccelStepper.h>

const int LEFTBUTTONPIN = 11;
const int MIDBUTTONPIN = 12;
const int RIGHTBUTTONPIN = 13;
const int LEFTSTEPPIN = 2;
const int LEFTDIRPIN = 3;
const int MIDSTEPPIN = 4;
const int MIDDIRPIN = 5;
const int RIGHTSTEPPIN = 6;
const int RIGHTDIRPIN = 7;

AccelStepper myMotorLeft(1, LEFTSTEPPIN, LEFTDIRPIN);
AccelStepper myMotorMid(1, MIDSTEPPIN, MIDDIRPIN);
AccelStepper myMotorRight(1, RIGHTSTEPPIN, RIGHTDIRPIN);
QMC5883LCompass compass;

int posX = 0;
int posY = 0;
int posZ = 0;
int spin = 3000;

int x, y, z;
boolean leftIsSpinning = false;
boolean leftGetResults = false;

boolean midIsSpinning = false;
boolean midGetResults = false;

boolean rightIsSpinning = false;
boolean rightGetResults = false;

unsigned long timer = 0;
const int INTERVAL = 1000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  compass.init();

  pinMode(LEFTBUTTONPIN, INPUT);
  pinMode(MIDBUTTONPIN, INPUT);
  pinMode(RIGHTBUTTONPIN, INPUT);

  myMotorLeft.setMaxSpeed(1000); 
  myMotorLeft.setAcceleration(500);

  myMotorLeft.setSpeed(1000);

  myMotorMid.setMaxSpeed(1000);
  myMotorMid.setAcceleration(500);

  myMotorMid.setSpeed(1000);

  myMotorRight.setMaxSpeed(1000);
  myMotorRight.setAcceleration(500);

  myMotorRight.setSpeed(1000);
}

void loop() {
  // put your main code here, to run repeatedly:

  if(digitalRead(LEFTBUTTONPIN) == !HIGH) {
    leftIsSpinning = true;
  } else {
    leftIsSpinning = false;
  }

  if(digitalRead(MIDBUTTONPIN) == !HIGH) {
    midIsSpinning = true;
  } else {
    midIsSpinning = false;
  }

  if(digitalRead(RIGHTBUTTONPIN) == !HIGH) {
    rightIsSpinning = true;
  } else {
    rightIsSpinning = false;
  }
  
  if(leftIsSpinning) {
    compass.read();
    x = compass.getX();
    myMotorLeft.runSpeed();
    leftGetResults = true;
  }

  if(midIsSpinning) {
    compass.read();
    y = compass.getY();
    myMotorMid.runSpeed();
    midGetResults = true;
  }

  if(rightIsSpinning) {
    compass.read();
    z = compass.getZ();
    myMotorRight.runSpeed();
    rightGetResults = true;
  }

  if(leftGetResults && !leftIsSpinning) {
    if(x < -3000) {
      x = -3000;
    } else if (x > 3000) {
      x = 3000;
    }
    posX = map(x, -3000, 3000, 1, 10);
    posX = 20*posX - 10;

    Serial.print("current x compass: ");
    Serial.println(x);
    Serial.print("move to Position: ");
    Serial.println(posX);
    
    myMotorLeft.moveTo(posX);

    leftGetResults = false;
  }

  if(midGetResults && !midIsSpinning) {
    y = (y+x)/2;
    if(y < -3000) {
      y = -3000;
    } else if (y > 3000) {
      y = 3000;
    }
    posY = map(y, -3000, 3000, 1, 10);
    posY = 20*posY - 10;

    Serial.print("current y compass: ");
    Serial.println(y);
    Serial.print("move to Position: ");
    Serial.println(posY);
    
    myMotorMid.moveTo(posY);

    midGetResults = false;
  }

  if(rightGetResults && !rightIsSpinning) {
    y = (Z+y+x)/3;
    if(z < -3000) {
      z = -3000;
    } else if (z > 3000) {
      z = 3000;
    }
    posZ = map(z, -3000, 3000, 1, 10);
    posZ = 20*posZ - 10;

    Serial.print("current z compass: ");
    Serial.println(z);
    Serial.print("move to Position: ");
    Serial.println(posZ);
    
    myMotorRight.moveTo(posZ);

    rightGetResults = false;
  }
  
  if(myMotorLeft.distanceToGo() != 0) {
    myMotorLeft.run();
  } else {
    myMotorLeft.setSpeed(1000);
  }

  if(myMotorMid.distanceToGo() != 0) {
    myMotorMid.run();
  } else {
    myMotorMid.setSpeed(1000);
  }

  if(myMotorRight.distanceToGo() != 0) {
    myMotorRight.run();
  } else {
    myMotorRight.setSpeed(1000);
  }
}

 

]]>
Project 2: Meiger Counters https://courses.ideate.cmu.edu/62-362/f2021/project-2-meiger-counters/ Fri, 12 Nov 2021 17:45:09 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11779 Meiger Counters
Meiger Counter pointing at a ceiling ligtht

Meiger Counter pointing at a ceiling ligtht

Description

A plain, black carrying case usually meant for industrial tools with two cutouts in the foam.

Perfectly fit into the cutouts are two handheld meter-shaped devices, with boxy tops and a yellowed, plastic look. The handheld portions are opaque, while the top and bottom caps are made of a semi-translucent plastic. When taken out of their case and held in one hand, the user can see a meter labeled “magnetic field” going from 1 to 5 on the back. The front face on both meters is covered with an old, paper grid of red lines on yellowed paper. For one of the meters, the front features a coil wound around a stick, while on the other, a flat coil sits flush on the front surface.

The meters emit a click noise intermittently, around once or twice a second. When a user holds them close to an electrical device, the meters begin to click much more rapidly, blending together into a stream of pulses. Most devices will trigger this faster click rate: lights, wall adaptors, chargers, computers. The LEDs flicker whenever a click is emitted, and the meter’s hand on the back corresponds roughly to the clicking rate. 

 

Final Photos + Function

Removing Meiger counters from their carrying case

Removing Meiger counters from their carrying case

Mid-presentation: Andy standing in front of a slide which shows a graph of cancer rate vs childhood magnetic field exposure

Mid-presentation: Andy standing in front of a slide which shows a graph of cancer rate vs childhood magnetic field exposure

2nd generation Meiger counter, with a flat pack coil and round LED on top

2nd generation Meiger counter, with a flat pack coil and round LED on top

The two prototype Meiger counters in their carrying case, with the batteries removed and stored in a separate compartmenta

The two prototype Meiger counters in their carrying case, with the batteries removed and stored in a separate compartmenta

 

Process Reflection

I began this project like I usually do; procrastinating in the name of planning. Wait, honestly, I’m not sure that’s true. Making a jig helps you make a cabinet later, and proper planning prevents for potential problems. Anyway, Erin gave me a kick in the ass when she accused me of planning so I would never have to start working. Blunt works well with me, I started later that day. Because of how early I finished my first prototype, I finally had the chance to make a 2nd iteration of something. 

Electronics – Fairly simple, thanks to about $50 of adafruit boards lying around the physcomp lab. I struggled a bit miniaturizing everything to fit into the shell of the barcode scanner, but I switched to a smaller microcontroller and had a good time with it. 

Physical construction – Watching Tom’s project from last time, I realized that it’s easy to make something look good if you’re just repurposing something that already existed. I’m telling you this at risk to my own grade because I think it’s a bit exclusionary not everyone is into vintage consumer electronics or has the money to buy them, but they do look good. And yes, while it’s possible to use class budget to buy these things, it’s more of a mindset where you’re aware that you can trade money for part of your project to look good already. 

Anyway, I had these leftover handles from the last handheld barcode scanner to include a full sized HeNe tube to generate the laser. I needed to replace both the bottom cap and the top section, and luckily I had a 3D printer with just the right color of resin. The prints each took about 4 hours, and the first one did not fit properly. Good thing I had a second iteration! Little traps in the physical design – the speaker grills I added did not work, and the ammeter did not initially fit into the shell. These were things I forgot to fix in my 2nd go at it, probably because I just never wrote them down. 

Also working with foam is hard, and the cutouts I made didn’t look very smooth at all. Sanding the case’s letters off also took a long time. Overall, I think the case and the presentation helped set up the ambiance I wanted to create. And I’m glad people walked around with the Meiger counters and explored the evil electromagnetic devices around them; that’s the point of my project! However, most people in the class looked bored/sleepy when I presented, probably because they had just stayed up all night to finish their projects! I should have thought of that, but I wanted to try something more serious. I can do playful & tongue-in-cheek just fine. I still think that part didn’t really work out; I felt rushed while presenting because I didn’t want to bore anybody. Not sure how to fix that, presentations are either boring or funny. 

I wanted to try something serious because I know that it will be important. One day I will not be young, and people will not give me a pass to be childlike. I want to always be childlike, so full of wonder and excitement about the world around me, but seriousness is required for adult situations and I’ve never practiced that. The Little Prince is a great book to read yearly. Anyway, not sure I pulled that one off properly. Maybe I’m just not cut out for seriousness and should instead pursue some alternative method to gain ethos while presenting something. Any ideas welcome! 

In-Progress Photos

Guts of the first Meiger counter, not yet placed into the shell. Bottom cap of the Mieger counter is sitting next to it

Guts of the first Meiger counter, not yet placed into the shell. Bottom cap of the Mieger counter is sitting next to it. Features antenna, speaker, and amplifier.

Miniaturized guts, same parts as the previous photo except with a Seeeduino Xiao instead of an Arduino. This uC is crazy good, and it is tiny. I'm just beginning to glue everything down inside the 2nd iteration of the Meiger

Miniaturized guts, same parts as the previous photo except with a Seeeduino Xiao instead of an Arduino. This uC is crazy good, and it is tiny. I’m just beginning to glue everything down inside the 2nd iteration of the Meiger

Resin printed top shell of the Meiger counters, right after curing. Needs sanding.

Resin printed top shell of the Meiger counters, right after curing. Needs sanding.

Failure of white paint to cover the initial labels of the ammeters. I ended up printing out "magnetic field" labels and putting them over the originals

Failure of white paint to cover the initial labels of the ammeters. I ended up printing out “magnetic field” labels and putting them over the originals

Removing the DeWalt logo using sandpaper. I should've used acetone, it would have been much faster

Removing the DeWalt logo using sandpaper. I should’ve used acetone, it would have been much faster

Prepping the foam to fit into the case. I had to cut down much of the foam for it to even go in the bounds, and even then it looked a bit patchy.

Prepping the foam to fit into the case. I had to cut down much of the foam for it to even go in the bounds, and even then it looked a bit patchy.

#define speakerNum 7
#define dialNum 3
#define trigger 6


float curRad = 0.0;
long now = 0;

// Click timing
int lambda = 4;
int next = 0;

// Click rate measurement
float rate = 0.0;
int numInLast100 = 0;
long timeSinceLast100 = 0;

void setup() {
    pinMode(speakerNum, OUTPUT);
    pinMode(dialNum, OUTPUT);
    pinMode(trigger, INPUT_PULLUP);
    Serial.begin(115200);
    
}


// Storing the samples that we read in
const int numsamples = 100;
double yea[numsamples];
int ind = 0;

void loop() {
    // Calculate the exponential timing for the geiger counter
    if ((millis()-now) > next){
        next = -log(1.0-float(random(1000))/1000.0)/lambda*1000;
        if (!digitalRead(trigger)){
            digitalWrite(speakerNum, HIGH);
        }
        delayMicroseconds(40);
        digitalWrite(speakerNum, LOW);
        now = millis();
        numInLast100 += 1;
    }

    // Calculate rate to output to the ammeter
    if (millis()-timeSinceLast100 > 100){
        rate = float(numInLast100)*100*.3 + rate*.7;
        analogWrite(dialNum, min(rate,255.0));
        numInLast100 = 0;
        timeSinceLast100 = millis();
    }

    yea[ind] = analogRead(A0);
    
    ind += 1;
    ind %= numsamples;
    float A0var = variance(yea, numsamples);
    // set the new clicking rate based on the variance of the EMI around it
    lambda = sqrt(min(map(A0var, 0, 1000, 1,30),500));
    Serial.println(lambda);
    
}




// Calculates the variance of a list of numbers
float variance(double a[], int n) {   
    // Compute mean (average of elements) 
    double sum = 0; 
    
    for (int i = 0; i < n; i++) sum += a[i];    
    double mean = (double)sum / (double)n; 
    // Compute sum squared differences with mean. 
    double sqDiff = 0; 
    for (int i = 0; i < n; i++) 
        sqDiff += (a[i] - mean) * (a[i] - mean); 
    return (float)sqDiff / n; 
}

 

 

]]>
Malleable Melodies | Project 2 FLOW Documentation | kirmanh https://courses.ideate.cmu.edu/62-362/f2021/malleable-melodies-project-2-flow-documentation-kirmanh/ Fri, 12 Nov 2021 03:15:43 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11727

Setup with no people

People using malleable melodies during the critique

Showing base setup including how wires come out of the bottom of the post

wiring

Judenique using one of the playdough modules during the critique. Shows the underside of the plate with the alligator clips connecting to the aluminum tape nodes feeding into the post.

People made lots of funny sculptures as they interacted with the project

 

Description

There are 5 yellow posts with some plates and colorful lumps on top. They are all different heights. Each one has a bunch of wires coming out of the bottom that connect to a red box in the middle. There is some strange music playing with a bunch of different sounds. If you change the shape of the colorful lumps (they are mushy) then one of the sounds changes. Each lump changes a different sound in the collection of sounds playing. Because there are 5 you can work with other people to change the different sounds together.

Process

The very first playdough module. Just testing to see if it could work.

First encounter with the corrosion problem. The playdough removed the nice shiny coating from the spring and got dirty.

All of the demo modules I built over the process. You can see the heavy corrosion on the early copper modules as compared to the aluminum one.

Initial “mockup” of the planned installation with a cardboard box acting as the “mother” module into which I would put the computer, arduino, and breadboard.

whiteboard doodles trying to figure out how to deal with the wires coming from the plates and feeding out to the base.

 

I think one of the main things I learned from this project was the importance of presentation. In my previous project I didn’t focus as much on the “format” of the project, instead I focused more on the “guts,” but in this project I spent a lot of time mocking up various ways of arranging things and I think it really paid off. I also learned the importance of conceptual development not just in the initial ideation stages but also in regards to the overall process. I think something I tend to lose sight of as I work is why I am actually doing what I am doing or what the goal is besides just “finishing” the project, and so when it comes time for the critique I felt like the conceptual underpinning of my project didn’t really match up as well with the experiential aspect. For example my goal was to create a more “flexible” music manipulation software, however I ended up scripting the music people were using, or even having the option of a pause/play feature which I think would have better aligned with that goal. I think if I had set up all five modules earlier and had had people interact with the overall piece at the 90% critique it would have really helped me hone that last 10% to be more aligned with my overall goal.

There were also quite a number of unexpected speed bumps that slowed down the project’s development such as the fact that the playdough proved to be extremely corrosive and would become extremely discolored over time as well as the fact that it is apparently really hard to get the serial port to communicate with p5.js. Despite these setbacks however I think the project was also successful in that users seemed really engaged and drawn to the project, which poses well for further development. I also learned A TON doing this project; I learned how to better plan out and prioritize physical presentation early on, I learned a lot more about how to find and successfully modify code found online to your needs,  I learned to have more conceptual discussions throughout the process rather than just at the start/end, and I learned a whole new software.

If I were to continue with this project I’d like to 1) redo the central box to fit in the speakers 2) add some pulsing lights to the center box so it becomes more like a “mother” and the modules part of some sort of superorganism 3) think more about the “flexibility” aspect of the project and maybe modify it so people have some sort of sound selection 4) consider collaborative aspects of the project more and potentialities of different playdough modules interacting with one another.

Code

*** you need to use the program p5.serialcontrol (https://github.com/p5-serial/p5.serialcontrol/releases) in order to use this code. Otherwise arduino code won’t be able to talk to the p5.js code. ***

Arduino Code:

//code for "Malleable Melodies" project by Kirman Hanson as part of 62-362
//code modified from:https://gist.github.com/shfitz/0aabb07daa8ef84904d6e3c6a17381a0
//if you try to get rid of the inbyte feature it won't work 

int firstSensor = 0;    // first analog sensor
int secondSensor = 0;   // second analog sensor
int thirdSensor = 0;
int fourthSensor = 0;
int fifthSensor = 0;
int sixthSensor = 0;    // digital sensor, need this for the code to work
int inByte = 0;         // incoming serial byte

void setup() {
  // start serial port at 9600 bps and wait for port to open:
  Serial.begin(9600);
  while(!Serial){
    ;;
  }

  pinMode(2, INPUT);   // digital sensor is on digital pin 2
  establishContact();  // send a byte to establish contact until receiver responds
}

void loop() {
  // if we get a valid byte, read analog ins:
  if (Serial.available() > 0) {
    // get incoming byte:
    inByte = Serial.read();
    // read first analog input:
    firstSensor = analogRead(A0);
    // read second analog input:
    secondSensor = analogRead(A1);
    // read third analog input:
    thirdSensor = analogRead(A2);
    // read fourth analog input:
    fourthSensor = analogRead(A3);
    //read fifth analog input:
    fifthSensor = analogRead(A4);
    // read switch value
    sixthSensor = digitalRead(2);
    
    // send sensor values:
    Serial.print(firstSensor);
    Serial.print(",");
    Serial.print(secondSensor);
    Serial.print(",");
    Serial.print(thirdSensor);
    Serial.print(",");
    Serial.print(fourthSensor);
    Serial.print(",");
    Serial.print(fifthSensor);
    Serial.print(",");
    Serial.println(sixthSensor);
  }
  
}

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("0,0,0");   // send an initial string
    delay(300);
  }
}

p5.js:

//code for "Malleable Melodies" project by Kirman Hanson as part of 62-362

// pitch shift by Anthony T. Marasco [2018] originally from https://codepen.io/lsuddem/pen/RJEYLr

// serial communication with a microcontroller sending multiple values from: https://editor.p5js.org/shfitz/sketches/9dPvGrtEZ

// arduino code [for serial communication] can be found here : https://gist.github.com/shfitz/0aabb07daa8ef84904d6e3c6a17381a0

let serial; // variable for the serial object
let sensors = [255, 255, 255, 255, 255, 255]; // array to hold data from arduino

//sound stuff
let shifter;
let player;
let footPlayer;
let footShifter;
let drumSound;
let drumSound2;
let keySound;
let footsSound;
let bellSound;
let pitchMax = 8.0;
let pitchMin = -15.0;
let bellRateMax = 4.0;
let volMax = 1.0;
let volMin = 0.0;
let footVolMax = 0.7;
let footVolMin = 0.0;
let rateMax = 1.5;
let rateMin = 0.5;
    
//doughVals
let pD1Map, pD2Map, pD1Round, pD2Round, pD3Map, pD3Round;
//pD4 controls the bells sound volume and rate
let pD4VolMap, pD4VolRound, pD4RateMap, pD4RateRound;
//pD5 controls the footsteps volume and pitch
let pD5PitchMap, pD5PitchRound,pD5VolMap,pD5VolRound;

function preload() {
  drumSound = loadSound('drumming.wav');
  keySound = createAudio('keys.wav');
  footsSound = createAudio('footsteps.mp3');
  bellSound = loadSound('bells.wav');
  print('preloading');
  //storm pitch shift
  shifter = new Tone.PitchShift(0).toMaster();
    shifter.windowSize = 0.03
  player = new Tone.Player('storm.mp3').connect(shifter);
  player.loop = true;
  //foot pitch shift
  footShifter = new Tone.PitchShift(0).toMaster();
    footShifter.windowSize = 0.03
  footPlayer = new Tone.Player('footsteps.mp3').connect(footShifter);
  footPlayer.loop = true;
}


function setup() {
  createCanvas(windowWidth, windowHeight);
  textAlign(CENTER);
  textFont("Helvetica");
  fill(0);
  
//serial stuff
  // serial constructor
  serial = new p5.SerialPort();

  // serial port to use - you'll need to change this
  serial.open('/dev/tty.usbmodem14101');

  // what to do when we get serial data
  serial.on('data', gotData);

  // when to do when the serial port opens
  serial.on('open', gotOpen);

//sound stuff
  
  drumButton = createButton("Start Drum");
  drumButton.position(width / 2 - 50, height / 2);
  drumButton.mousePressed(play1);
  
  stormButton = createButton("Start Storm");
  stormButton.position(width/2-50, height/2-100)
  stormButton.mousePressed(play2)
  
  keysButton = createButton("Start Keys");
  keysButton.position(width/2-50, height/2-200)
  keysButton.mousePressed(play3)
  
  footsButton = createButton("Start Footsteps");
  footsButton.position(width/2-50, height/2-250)
  footsButton.mousePressed(play4) 
  
  bellButton = createButton("Start Bells");
  bellButton.position(width/2-50, height/2+100)
  bellButton.mousePressed(play5) 
  
  shiftSlider = createSlider(pitchMin, pitchMax, 1, 0.1);
  shiftSlider.style("width", "200px");
  shiftSlider.position(width / 2 - 100, height / 2 + 70);
  
  footShiftSlider = createSlider(pitchMin, pitchMax, 1, 0.1);
  footShiftSlider.style("width", "200px");
  footShiftSlider.position(width / 2 - 100, height / 2 + 170);
  
}

function draw(){
  background(255);
  noStroke();
  
 //pitch controls
  shiftSlider.value(pD1Round);
  shifter.pitch = shiftSlider.value();
  
  footShiftSlider.value(pD5PitchRound);
  footShifter.pitch = footShiftSlider.value();
  
  //rate controls
  bellSound.rate(pD4RateRound);
  drumSound.rate(pD3Round);
  
  //volume controls
  keySound.volume(pD2Round);
  //bellSound.volume(pD4VolRound);
  //drumSound.volume(0.7);
  footsSound.volume(pD5VolRound);
  
  fill(0);
  text('pot1: ' + sensors[0] + ', pot2: ' + sensors[1] + ', pot3: ' + sensors[2] + ', pot4: ' + sensors[3]+', pot5: ' + sensors[4],300,10);
  console.log(sensors)
}

function gotData() {
  let currentString = serial.readLine(); // store the data in a variable
  trim(currentString); // get rid of whitespace
  if (!currentString) return; // if there's nothing in there, ignore it
  sensors = split(currentString, ',');
  
  //map pD1 to pitch for storm
  pD1Map = map(float(sensors[0]), 200, 800, pitchMin, pitchMax);
  pD1Round = round(constrain(pD1Map, pitchMin, pitchMax),1);
  
  //map pD2 to volume for keys
  pD2Map = map(float(sensors[1]), 95, 300, volMin, volMax);
  pD2Round= round(constrain(pD2Map, volMin, volMax), 1);
  
  //map pD3 to rate for drums
  pD3Map = map(float(sensors[2]), 100, 300, rateMin, rateMax);
  pD3Round= round(constrain(pD3Map, rateMin, rateMax), 1);
  
  //map pD4 to volume and rate for bells
  pD4VolMap = map(float(sensors[3]), 100, 500, volMin, volMax);
  pD4VolRound= round(constrain(pD4VolMap, volMin, volMax), 1);
  pD4RateMap = map(float(sensors[3]), 100, 500, rateMin, bellRateMax);
  pD4RateRound= round(constrain(pD4RateMap, rateMin, bellRateMax), 1);
  
  //map pD5 to volume and pitch for foots
  pD5VolMap = map(float(sensors[4]), 100, 300, footVolMin, footVolMax);
  pD5VolRound= round(constrain(pD5VolMap, footVolMin, footVolMax), 1);
  pD5PitchMap = map(float(sensors[4], 100, 300, pitchMin, pitchMax),1);
  pD5PitchRound = round(constrain(pD5PitchMap, pitchMin, pitchMax), 1);
  
  //console.log("pD1Round:"+pD1Round);
  serial.write('A');
}

function gotOpen() {
  print("Serial Port is Open");
  serial.clear(); // clears the buffer of any outstanding data
  serial.write('A'); // send a byte to the Arduino
}

function play1() { // set up as drum & rate button
  drumSound.loop();
}
function play2(){ //set up as storm & pitch player
  player.start();
}
function play3(){//set up as keys/volume player
  keySound.loop();
}
function play4(){//set up as footsteps/volume player
  footsSound.loop();
  footPlayer.start();
}
function play5(){//set up as footsteps/volume player
  bellSound.loop();
}

p5.js index code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="path/to/p5.sound.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
   	<script language="javascript" type="text/javascript" src="https://cdn.jsdelivr.net/npm/p5.serialserver@0.0.28/lib/p5.serialport.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/13.2.0/Tone.min.js"></script>
    
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <script src = "comboSketch.js"></script>
  </body>
</html>

 

]]>