Documentation – 62-362 Fall 2020 https://courses.ideate.cmu.edu/62-362/f2020 Electronic Logics && Creative Practice Tue, 29 Dec 2020 23:09:52 +0000 en-US hourly 1 https://wordpress.org/?v=5.4.15 An Extension of Us https://courses.ideate.cmu.edu/62-362/f2020/an-extension-of-us/ Tue, 15 Dec 2020 03:19:03 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10587 For this piece, “An Extension of Us,” I created two braids where those using it can communicate with each other using a series of inputs and outputs. When one person brings their hand close to the braid, the light will turn on for both of them, when one person squeezes the braid, the other will feel a buzz, and when both people sing loudly enough a song will play for both of them.

An Extension of Us

made in collaboration with my sister Ivana

For a braid to hold, its strands must conform to a gradual taper, or else, without a rubber band or ribbon tightened to its end, it unravels.

In some cases, change must be abrupt. In others, we force it so, getting the pattern all wrong and having to start over.

And I know that there’s no way you would ever leave me stranded, even though I question whether this blood is sufficient reason to keep our lives intertwined. Even though I’m unsure of the difference between intertwined and bound. Even though I’m not sure if those differences even matter. 

A braid, woven tightly enough, becomes rigid, any unevenness obvious.

Think about the time spent crossing these strands, millions of them by now, and think of the banality. Think of the time spent in each other’s presence, chattering to distract, our bond strengthening over days as our fingers weakened over hours, this rite of passage now the basis of our livelihoods.

Question: How many times, in our lives, will we undo? Start over? 

Answer: Every time we notice the tangled mass of hair our lives have become. Hopefully.

I wonder how often you feel lonely when I’m around. I wonder if you ever look at me and think: You’ll never understand.

I am trying to hold the small things and remember that they make up the bulk of our time here and sit, ensconced in the loveliness of them and notice that they are there, innumerable, they’re fine strands intertwined, knotted, waiting to be brushed free.

I am trying not to relegate the good only to memories.

Initially I had wanted to create three braids for myself, my mother and my sister, but ended up creating two. While building this I kept forgetting how to set up the wires and also how to debug which led to an even longer process when getting to the end of it. In the beginning I had planned on having the two braids communicate with one another from a distance, unfortunately there was a problem with setting up the software correctly so then decided to wire the arduinos together so that they could communicate with each other. One of the hardest parts was actually braiding everything together, something that I thought would actually be the easiest. One of the first problems that I had was the placement of everything, which ended up being too long for the braid. This was mainly because the pressure sensor was at the end and since the braid tapered at the end it was not enough to cover it well enough. While I was braiding I kept getting frustrated because of how the hairs kept snagging onto the different pieces, after my like fifth attempt or so I decided to separate the hair and create three separate braids before braiding everything together. Doing this created a different, unique braid to what is usually done in hairstyles, and especially because of all the components that were in which is something that I liked. When I was connecting the braid to my roommate’s hair I expected it to be much heavier than it was but the braid did not feel heavy at all for neither of us, but the fact that we have both had hair extensions throughout most of our lives I’m sure helped with that. When it comes to the portion with the arduino I definitely learned a lot considering how this was my first time interacting with it all. Overall it was a taxing and eye opening project and I really enjoyed it despite all of the hurdles that I experienced.

Arduino Code:

//Big ting
#include “Arduino.h”
#include “SoftwareSerial.h”
#include “DFRobotDFPlayerMini.h”
SoftwareSerial speakerSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

SoftwareSerial vals2(2, 3); // RX, TX

int RXPIN = 2;
int TXPIN = 3;
int distanceVal2;
int pressureVal2;
int svTotal2;

int soundDetector = A2;
int distanceSensor = A4;
int pressurePin = A5;
int ledPin = 5;
int motorPin = 7;

void setup() {
pinMode(distanceSensor, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

pinMode(pressurePin, INPUT);
pinMode(motorPin, OUTPUT);
digitalWrite(motorPin, LOW);

vals2.begin(9600);
speakerSerial.begin(9600);
Serial.begin(9600);

Serial.println();
Serial.println(F(“DFRobot DFPlayer Mini Demo”));
Serial.println(F(“Initializing DFPlayer … (May take 3~5 seconds)”));
if (!myDFPlayer.begin(speakerSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F(“Unable to begin:”));
Serial.println(F(“1.Please recheck the connection!”));
Serial.println(F(“2.Please insert the SD card!”));
while(true);
}
Serial.println(F(“DFPlayer Mini online.”));
myDFPlayer.volume(20); //Set volume value. From 0 to 30*/
}

long svTotal=0;

void loop() {
vals2.listen();
int distanceVal = analogRead(distanceSensor);
int pressureVal = analogRead(pressurePin);
int soundVal = analogRead(soundDetector);
for (int i = 0; i <= 100; i++){
svTotal += soundVal;
delay(1);
}
svTotal=svTotal/100;
//Serial.println(“sound val total”);
//Serial.println(svTotal);

//vals2.println(“hm”);
vals2.println((String)distanceVal+”,”+pressureVal+”,”+svTotal);
if (vals2.available()){
Serial.println(“vals2 data”);
distanceVal2 = vals2.parseInt();
pressureVal2 = vals2.parseInt();
svTotal2 = vals2.parseInt();
}
Serial.print(“distance val: “);
Serial.println(distanceVal); /*
Serial.println(“pressure val”);
Serial.println(pressureVal);
Serial.println(“sound val”);
Serial.println(soundVal);*/

//distance -> light
if ((distanceVal > 10)||(distanceVal2 > 50)) {
digitalWrite(ledPin, HIGH);
delay(3000);
}
digitalWrite(ledPin, LOW);

//pressure -> buzz
if (pressureVal2 > 800){
digitalWrite(motorPin, HIGH);
delay(500);
}
digitalWrite(motorPin, LOW);

//sound -> music
if ((svTotal > 300) && (svTotal2 > 300)) {
myDFPlayer.play(1);
delay(10000);
myDFPlayer.next();
}
svTotal = 0;

}

]]>
Tongue Pals https://courses.ideate.cmu.edu/62-362/f2020/project-no-2-john/ Sat, 14 Nov 2020 11:44:11 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10382 Two pals enjoy a romantic candlelit evening.

When the flame sensor attached to Puppet A’s arm detects candlelight, Puppet A will slowly extend their tongue into Puppet B’s mouth, closing a circuit that powers the LEDs in Puppet B’s eyes. Careless Whisper will play/pause when the flame sensor is on/off. Puppet A’s eyes will also light up and their eyebrows will furrow the further their tongue extends.

Reflection:

In this time of social distancing, we lack the ability to reach out and touch one another as we once did, whether these relationships be mundane or intimate is irrelevent as they are all a vital part of our human nature, which up until now we have taken for granted.

I wanted to create an interaction between two puppets/animatronics, but didn’t want to over complicate said interaction. Ideally, I would have liked to include a conversation between the two puppets, however due to the lack of time and the scope of the assignment, I decided to move forward with a non-verbal interaction.

I considered preparing audio for this piece, but as far as I know, the dfmini is unable to play multiple mp3 files at the same time. So if I wanted both puppets to speak at once, I would have to put both voices on the same mp3 file, and manually sync that with the puppets respective mouths. I thought that it might be too much of an undertaking.

After some sketching, I landed on a funny little idea, where by extending one puppets tongue into the others mouth, the eyes of the puppet light up. This would be triggered by candlelight, with some goofy music to set the mood.

Thinking of all the obstacles people go through to create a connection, I thought it might be fun to add some more hurdles for my automaton representation.

I was moving the hand (with the flame sensor) around manually, but I could have had it triggered by some different input, and turn this into some sort of Rube Goldberg machine. For instance, the hand will only move into position after 8pm, the tongue will only extend if the hand is next to a flame, and the eyes will only light up if the puppets are near enough for the tongue to touch the copper tape in Puppet B’s mouth. Or maybe it has to be the third date, and the lighting has to be in a certain range. Just adding more reasons for things to go wrong, just like they do in real life.

It was interesting to hear others interpretations of this in class. It was not my intention to make something uncomfortable to watch, but I understand how it can be. Upon reflection, I found that, with very minor changes, these animatronics could represent much darker themes if I so choose. I think it goes to show the power of the face, and a puppets ability to mimic them. When all is said and done, it’s just some foam glued to some cardboard, but when it’s put together in a certain way, we can attach meaning and emotion to it.

Since I was experimenting with making these puppets, I thought I would play with the shapes a little more, thus the recoiled position of Puppet B. If I had some foresight, I probably would have realized that it might end up looking a little alarming.

Upon further reflection I decided it was unproductive to assign genders to the two. I don’t view either of them in any way, and in my mind, it’s up to the viewer to make those assumptions if they choose. Again, this was meant to be a lighthearted project, but I feel that under a heteronormative assumption, there are issues with the act of giving and the act of receiving, those being roles stereotypically anchored to men and women respectively.

It was a cool project, and I learned a lot. Definitely gained some perspective, specifically that I need to pay more attention to others perspectives.

Electronics:

    • 2 Arduinos (I found it easier to use a second Arduino for audio)
    • Flame sensor
    • Servo
    • Micro Servo
    • 2 red LED, 2 yellow LED
    • Dfmini
    • Speaker
    • Wires, bread boards, resistor

Materials:

    • Chipboard (cereal boxes etc.)
    • Mattress foam
    • Oil pastels
    • plastic cup
    • Lego gears, beams, axles, and pins
    • Hot glue
    • Copper tape
    • Electrical tape
Code:

Movement:

#include <Servo.h>

Servo tongue;
Servo brow;

int flame_sensor = 3;
int angle = 170;
int old_angle;

int brow_angle = 90;


void setup() {
  pinMode(flame_sensor, INPUT);
  tongue.attach(5);
  brow.attach(6);

  brow.write(brow_angle);
  tongue.write(angle);
}

void loop() {
  old_angle = angle;
  
  if (digitalRead(flame_sensor) == HIGH) { 
    if (angle < 170) {
      angle = angle + 2;
    }
  } else {
    if (angle > 10) {
        angle = angle - 2;
      }  
  }
  
  if (old_angle != angle) {
    brow_angle = map(angle, 10, 170, 45, 90);
    tongue.write(angle);
    brow.write(brow_angle);
  }
  delay(50);
}

Audio:

/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
 <https://www.dfrobot.com/index.php?route=product/product&product_id=1121>

 ***************************************************
 This example shows the basic function of library for DFPlayer.

 Created 2016-12-07
 By [Angelo qiao](Angelo.qiao@dfrobot.com)

 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/

/***********Notice and Trouble shooting***************
 1.Connection and Diagram can be found here
 <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
 2.This code is tested on Arduino Uno, Leonardo, Mega boards.
 ****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int flame_sensor = 3;

bool song_paused = false;

void setup()
{
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  
  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  pinMode(flame_sensor, INPUT);
  
  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
  myDFPlayer.play(1);  //Play the first mp3
}

void loop()
{
  static unsigned long timer = millis();
  if (digitalRead(flame_sensor) == LOW) { 
    if (song_paused == true) {
      myDFPlayer.start();
      song_paused = false;
    }
  } else {
    if (song_paused == false) {
      myDFPlayer.pause();
      song_paused = true;
    }
  }

  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

Alarm Clock:

An alarm clock determined to fulfill its purpose.

Servo in the mouth “synced” up with audio (close enough). Neo pixel ring light hidden behind eyes, color changes to red and the brightness increases for each “wake up”. Eyebrows also furrow a bit further with each “wake up”. Snooze can be triggered after each “wake up”, the audio will stop, eyebrows will raise, and the eyes will turn green.

Reflection:

This was a lot of fun to make.

Took awhile to get the mouth to move with the audio in a way that I wanted. The servo had to rotate fully to open the mouth, and I realized that it wasn’t moving fast enough to fully reach a position before the next command was given. So although the open angle and closed angle are the same for each word, the servo doesn’t always have enough time to fully extend. Only the last one where the “wake up” was dragged out does the mouth fully open.  I could have moved the servo so it wouldn’t have to rotate so much to open the mouth, but I thought that it kind of worked out for the best. Also the face kind of shakes when the mouth is fully opened which makes it look a little angrier, happy accident.

I wanted to add a lot more to this, at the moment it’s not really an alarm clock, it turns on with a button and turns off with another button. But I do plan on finishing it in the future.

Ideally, I wanted to add in a real time clock, an lcd to show the current time, some simple buttons to set the alarm, which would also be shown on the lcd, a motion sensor on top to act as a snooze button, and a passive buzzer to make it a little more disruptive to your sleep. I’d also like to give the face some color, and maybe add some thin rings around the eyes.

Also also, there are a few things I’d like to debug about this as it is now. The code for the audio is run on  a second Arduino, and I think it would be for the best if it wasn’t, but because the code for the mouth/eyebrows/eyes uses the delay function, I found it a lot easier to just separate the two. Also concerning sound, every time I’ve used speakers I end up with a weird clicking noise, even when no audio is playing. You can hear it at the end of the video. I’m sure there are simple solutions for these issues, and I look forward to figuring them out.

Build wise, ideally ideally, I would laser cut the body, use a kerf cut (I think it’s called that) to make the round part, probably add some supports, laser cut acrylic for the eyes, and laser cut or 3d print gears for the eyebrows and maybe mouth. The legos gears were useful but a bit limited, I had a small range of gear sizes and the positions they could be placed in. Also, cardboard and scissors are great for prototyping, but there’s no replacing the precision of a laser cutter, and wood also happens to looks nicer. Maybe I’ll try this again now that I have the experience.

Electronics:

    • 2 Arduino (I found it easier to use a second Arduino for audio)
    • Servo
    • Micro Servo
    • Neo pixel ring
    • Dfmini
    • Speaker
    • 2 buttons
    • Wires, bread boards, resistors

Materials:

    • Chipboard (cereal boxes etc.)
    • Mattress foam
    • plastic cup
    • Lego gears, beams, axles, and pins
    • Paper towel
    • Hot glue
    • Copper tape
    • Electrical tape
Code:

Movement:

#include "Arduino.h"
#include <Servo.h>

#include <Adafruit_NeoPixel.h>

#define PIN            9
#define NUMPIXELS      12

int alarm_button = 3;
int snooze_button = 4;

int mouthPin = 7;
Servo mouth;

int openAngle = 70;
int closedAngle = 170;

int browPin = 8;
Servo brow;

int brow_1 = 80;
int brow_2 = 100;
int brow_3 = 130;
int brow_4 = 145;
int brow_5 = 160;

bool wake_up = false;
int act = 1;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

uint32_t color_1 = pixels.Color(210,250,250); //light blue
uint32_t color_2 = pixels.Color(220,150,100); 
uint32_t color_3 = pixels.Color(230,100,25); 
uint32_t color_4 = pixels.Color(250,50,0);    //orange
uint32_t color_5 = pixels.Color(250,0,0);     //red
uint32_t color_6 = pixels.Color(0,250,0);     //green

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

  pixels.begin(); 
  pixels.setBrightness(0); 
  pixels.show(); 
  
  pinMode(alarm_button, INPUT);
  pinMode(snooze_button, INPUT);
  
  mouth.attach(mouthPin);
  mouth.write(closedAngle);

  brow.attach(browPin);
  brow.write(brow_1);
}

void loop() {

  if (wake_up == true) {

    // first wake-up
    if (act == 1) {
      pixels.setBrightness(70);
      pixels.fill(color_1);
      pixels.show();

      brow.write(brow_1);
      
      delay(790);
      
      //"wake up" at ~.85 seconds
      mouth.write(openAngle);
      delay(270);
      
      //close end word
      mouth.write(closedAngle);
      delay(1850); //1780 was a real sweet spot
    }
    
    if (act == 2) {
      pixels.setBrightness(100);
      pixels.fill(color_2);
      pixels.show();

      brow.write(brow_2);
      
      //"wake up" at ~3.1 seconds
      mouth.write(openAngle);
      delay(270);
     
      //close end word
      mouth.write(closedAngle);
      delay(1460);
    }
    
    if (act == 3) {
      pixels.setBrightness(120);
      pixels.fill(color_3);
      pixels.show();

      brow.write(brow_3);
      
      //"wake up" at ~4.8 seconds
      mouth.write(openAngle);
      delay(230);
      
      //close end word
      mouth.write(closedAngle);
      delay(1400);
    }
    
    if (act == 4) {
      pixels.setBrightness(160);
      pixels.fill(color_4);
      pixels.show();

      brow.write(brow_4);
      
      //"wake" at ~6.4 seconds
      mouth.write(openAngle);
      delay(220);

      //close end word
      mouth.write(closedAngle);
      delay(1400);
    }

    if (act == 5) {
      pixels.setBrightness(180);
      pixels.fill(color_5);
      pixels.show();

      brow.write(brow_5);
      
      //"wake" 
      mouth.write(openAngle);
      delay(310);
      //close between words
      mouth.write(closedAngle);
      delay(200);
      //"up"
      mouth.write(openAngle);
      delay(700);
      //close end word
      mouth.write(closedAngle);
      delay(1500);
    }
  
    act = act + 1;
    if (act > 5) {
      act = 1;
    }

  }

  if (digitalRead(alarm_button) == HIGH) {
    wake_up = true;    
  }
  
  if (digitalRead(snooze_button) == HIGH) {
    if (wake_up == true) {
      wake_up = false;
      
      pixels.setBrightness(120);
      pixels.fill(color_6);
      pixels.show();
      
      brow.write(brow_1);
      
      delay(1000);
    }
  }
}

Audio:

/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
 <https://www.dfrobot.com/index.php?route=product/product&product_id=1121>

 ***************************************************
 This example shows the basic function of library for DFPlayer.

 Created 2016-12-07
 By [Angelo qiao](Angelo.qiao@dfrobot.com)

 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/

/***********Notice and Trouble shooting***************
 1.Connection and Diagram can be found here
 <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
 2.This code is tested on Arduino Uno, Leonardo, Mega boards.
 ****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int alarm_button = 2;
int snooze_button = 3;
bool song_paused = true;

void setup()
{
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  
  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  pinMode(alarm_button, INPUT);
  pinMode(snooze_button, INPUT);
  
  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
}

void loop()
{
  static unsigned long timer = millis();
  if (digitalRead(alarm_button) == HIGH) { 
    if (song_paused == true) {
      myDFPlayer.play(2);
      song_paused = false;
    }
  } 
  
  if (digitalRead(snooze_button) == HIGH) { 
    if (song_paused == false) {
      myDFPlayer.stop();
    }
  } 
  
  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

Other:

During the process, I experimented with a couple different puppets, but eventually settled on those above. These are the other animatronics I made during this project, albeit without the heartbeat of an Arduino Uno.

Puppet E

Progress Pics:

]]>
What’s Your Level? https://courses.ideate.cmu.edu/62-362/f2020/project-no-2-whats-your-level/ Sat, 14 Nov 2020 00:46:25 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10364 What’s Your Level?

Where are we going? Where are we coming from? Being able to let go and lose control important, especially during times in which there is a lot of political and social pressure. The desire for control is a root cause to much of the power struggle in the world. Simplicity can be found in the smallest of tasks, processes which give us the illusion of control, little moments of victory. Keeping track of how much one has achieved isn’t healthy, scoring yourself only builds higher walls. As tempting as it is to yearn for the comfort of control, it is important to find that same comfort in being out of balance.

What little tweak, in retrospect, would’ve changed the direction entirely? This is an opportunity for you to reflect on your creative and technical growth through the project, and think about what growth you want to aim for next. This shouldn’t be a recital of your process, but rather a meaningful consideration of what you experienced during the creation of your piece, 2–4 paragraphs in length.

  • The part of this project that was most intuitive for me was the physical construction of the ramp. The code and electronics were where I struggled most. I learned a lot about working with photoresistors/making my own break beam sensor. I struggled a lot with getting everything to function properly. I think I am slowly building a sort of intuition for working with electronics which I am happy about.
  • The most challenging part of all this was planning out the construction of the ramp (even though it turned out to be the easiest part for me) as well as the combination of the electronics and the structure. The main worry I had was that the ramp would flex as someone was on it, causing the lasers to automatically move, no longer sending the beam straight to the sensors. There was no issue with that.
  • Unfortunately the biggest mistake I made was not predicting the amount of space I would need to speed up to the ramp in order to make it to the top. At such a slow speed it is difficult to balance and the slope is pretty harsh coming up the ramp.

/*
What's Your Level?
Julita Przybylska
This code controls the LEDs and Motor through singals picked up by the pins connected to the photorestistors. Low readings cause the the score to go up as well as affecting the amount the wheel spins. High readings mean that there is not skater present.
Digital Pins correspond to the lasers and LEDs
Analog pins correspond to the photoresistors
*/
#include <AccelStepper.h>

const int STEP_PIN = 12; // A4988 "STEP" pin wired to Arduino pin 2 (you can change this)
const int DIR_PIN = 13; // A4988 "DIRECTION" pin wired to Arduino pin 3 (you can change this)

// make an AccelStepper motor object. "myMotor" can be any name you'd like.
AccelStepper myMotor(1, STEP_PIN, DIR_PIN);

const int photo1 = A0;
const int photo2 = A1;
const int photo3 = A2;
const int photo4 = A3;
const int photo5 = A4;

const int light = 500;

const int led1 = 7;
const int led2 = 8;
const int led3 = 9;
//const int led4 = 10;
const int led5 = 11;

bool skaterPresent = false;
unsigned long timer = 0;

unsigned long score = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  //pinMode(led4, OUTPUT);
  pinMode(led5, OUTPUT);

  myMotor.setMaxSpeed(1000); // measured in steps per second
  myMotor.setAcceleration(500); // measured in steps per second squared
  Serial.begin(9600);

}
/*
  checking for a break beam bright or dark analog read
  if dark, then this means that something is blocking the beam and points are added to total score,
          if highest score is reached the motor spins 5 rotations, otherwise moves incrimentally
  if bright, then this means that the light is reaching the photoresistor
*/
void loop() {
  // put your main code here, to run repeatedly:
  //sense photo values
  int beam1 = analogRead(photo1);
  int beam2 = analogRead(photo2);
  int beam3 = analogRead(photo3);
  int beam4 = analogRead(photo4);
  int beam5 = analogRead(photo5);

  //decision; 0-1023
  //if a high value is read, add a value to total points and move motor to new position
    int motorPos = 0;
  int light1 = LOW;
  int light2 = LOW;
  int light3 = LOW;
  //int light4 = LOW;
  int light5 = LOW;

  if (millis() - timer >= 60000 && skaterPresent == true) {
    skaterPresent = false;
    motorPos = 0;
    if (score > 0) {
      light1 = LOW;
      light2 = LOW;
      light3 = LOW;
      //light4 = LOW;
      light5 = LOW;
      delay(800);
      light1 = HIGH;
      light2 = HIGH;
      light3 = HIGH;
      //light4 = HIGH;
      light5 = HIGH;
      score -= 1;
      delay(800);
    }
    delay(5000);
    score = 0;
  }

  if (beam1 < light) {
    if (skaterPresent == false) {
      skaterPresent = true;
      timer = millis();
    }
    score += 1;
    motorPos = 40;
    light1 = HIGH;
  }

  if (beam2 < light) {
    score += 2;
    motorPos = 80;
    light1 = HIGH;
    light2 = HIGH;
  }
  if (beam3 < light) {
    score += 3;
    motorPos = 120;
    light1 = HIGH;
    light2 = HIGH;
    light3 = HIGH;
  }
  /*
  if (beam4 < light) {
    score += 4;
    motorPos = 160;
    light1 = HIGH;
    light2 = HIGH;
    light3 = HIGH;
    //light4 = HIGH;
  }
  */
  if (beam5 < light) {
    score += 5;
    motorPos = 1000;
    light1 = HIGH;
    light2 = HIGH;
    light3 = HIGH;
    //light4 = HIGH;
    light5 = HIGH;
  }

  //do
  if (skaterPresent == true) {
    digitalWrite(led1, light1);
    digitalWrite(led2, light2);
    digitalWrite(led3, light3);
    //digitalWrite(led4, light4);
    digitalWrite(led5, light5);
    myMotor.moveTo(motorPos);
    myMotor.run();
  }

  Serial.println((String)"light values: " + beam1 + ", " + beam2 + ", " + beam3 + ", " + beam4 + ", " + beam5 + "\tmotorPos: " + motorPos);
  //Serial.println((String)"score: " + score);
}

 

Process:

]]>
Tweet Ticker https://courses.ideate.cmu.edu/62-362/f2020/project-no-2-tweet-ticker/ Fri, 13 Nov 2020 22:51:15 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10342 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> Vitals Alarm Documentation https://courses.ideate.cmu.edu/62-362/f2020/vitals-alarm-documentation/ Fri, 13 Nov 2020 21:59:04 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10323 The purpose of this device is to measure my vitals and the vitals of my computers (two at the moment) and then assess how I’m doing in relation to the computers. The input is numerical data in the form of CPU + GPU die temperatures from the two PCs, as well as my Galvanic Skin Response (GSR, used in polygraph tests), heart-rate, and sleep pattern (when I went to sleep and for how long I slept). The output is a message accompanied by a sound of my choosing.

The “front” of the device. Due to mounting issues, the final installation makes the keypad side the front of the device.

The “back” of the device. The small speaker was eventually ditched in favor of a full-range speaker from a laptop.

When all vitals are good…

When one of the PC’s vitals are bad (my desktop, in this case)…

When PC vitals are bad, but your vitals are OK…

When the PC vitals are OK, but your vitals are not (stress ended up being the bad vital)… some swearing because I thought I broke the GSR sensor… right before the project documentation was due…)

When both your vitals and the PC vitals are bad…

Progress Images

I had originally intended to use a heart-rate monitor that worked over an analog pin, but this was interfering with the GSR sensor, so I ended up using an I2C Pulse Oximeter to get the heart-rate.

An early prototype… this is a mess…

GSR and heart-rate monitor were reading wonky…

I got really stressed about the wonky readings, and then the GSR started reading properly… a lower value means you are more stressed (given the way this particular sensor was reading).

All assembled with a 3D printed structure for the Arduino and LCD.

A 9V battery apparently isn’t enough for this…

Process Reflection

Similar to my other endeavors, I procrastinated a lot on this. What I had ready for the initial due date was more like what I should have had ready a week prior for the 90% complete presentation. With that said, though, the device has finally taken shape, though not necessarily what I had envisioned. I had planned to mount this on the other side of my bed, but command strips were neither commanding nor stripping. Double-sided tape was a no-go as well. Nothing would stick to the bottom of the 3D printed mount for the Arduino Uno and LCD. Chances are, it just needs to be sanded to smooth it out.

Regardless, the device functions as intended (though the sensors can be temperamental at times. If I were to do this again differently, of course I’d change a few things. For starters, I’d take more time early on, but that can be said for all of my endeavors–see “I procrastinated A LOT on this.” But aside from that, I’d also use an Arduino Due or Mega, since I was starting to run out of dynamic memory (SRAM) on the Arduino Uno. If I start to implement long-term data logging with an SD card reader like I intended to originally and run into issues, then that’s what I’ll do. I did learn quite a bit about working with biometric sensors, though. They can be really finnicky. Both the GSR sensor and heart-rate monitor can read completely differently just based on how hard it’s pressed against your skin (moreso the GSR sensor).

Code

/*
   Vitals Alarm (62-362 Project 2)
   Seth Geiser (sgeiser)

   Collaboration: Any example code used can be found
   as follows:

   GSR Sensor: https://wiki.seeedstudio.com/Grove-GSR_Sensor/
   MAX30102 PulseOx: Example 5: Heart-rate
   Keypad: Same as Project 1, Workaholic's Clock
   DFPlayer Mini: Same as Project 1, Workaholic's Clock

   This code initializes a keypad, 20x4 I2C LCD, Pulse
   Oximeter, Grove GSR sensor, and a DFRobot DFPlayer
   Mini. The keypad is for entering the reported CPU
   and GPU die temperatures for my two computers, as
   well as when I went to sleep and how long I slept.
   The GSR sensor measures galvanic skin response,
   similar to a polygraph test. The Pulse Oximeter is
   currently only being used as a heart-rate monitor,
   since the SPO2 functionality appears to not be
   working right now (tested with the SPO2 example
   code.

   Pin mapping:

   pin      | mode       | description
   ---------|------------|------------
   SDA/SCL   I2C          MAX30102 PulseOx + 20x4 I2C LCD
   2-8       Drive/Sense  Keypad
   9         RX           DFPlayer Mini TX
   10        TX           DFPlayer Mini RX
   A0        INPUT        GSR Sensor (through Grove shield)
*/

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <MAX30105.h>
#include <heartRate.h>

SoftwareSerial mySoftwareSerial(9, 10); // RX, TX
DFRobotDFPlayerMini myDFPlayer;

MAX30105 particleSensor;

LiquidCrystal_I2C lcd(0x27, 20, 4);
// set the LCD address to 0x27 for 20x4 display

char customKey = 0;
const byte ROWS = 4;
const byte COLS = 3;
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {5, 3, 2, 7};
byte colPins[COLS] = {4, 8, 6};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

const byte RATE_SIZE = 4; // Increase this for more averaging. 4 is good.
byte rates[RATE_SIZE]; // Array of heart rates
byte rateSpot = 0;
long lastBeat = 0; // Time at which the last beat occurred

float beatsPerMinute;
int beatAvg;

const int GSR = A0;
int sensorValue = 0;
int gsr_average = 0;

int j = 0;
int jarvisCPUDieTemp;
int jarvisGPUDieTemp;
int tarsCPUDieTemp;
int tarsGPUDieTemp;
int sleepStart;
int sleepAmount;

// For accepting input from the keypad, these get converted
// to the ints above...
char temp[2];
char sleep[2];

bool pcVitalsGood = true;
bool humanVitalsGood = true;

void(* resetFunc) (void) = 0; // reset the Arduino when done
void setup() {
  lcd.init();
  lcd.backlight();
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);

  lcd.setCursor(0, 0);
  lcd.print(F("Arduino UNO R3 SMD"));
  lcd.setCursor(0, 1);
  lcd.print(F("Starting DFPlayer..."));
  lcd.setCursor(0, 2);
  lcd.print(F("Starting MAX30102..."));
  lcd.setCursor(0, 3);
  lcd.print(F("Please wait..."));
  delay(2000);
  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST))
    // Use default I2C port, 400kHz speed
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("MAX30102 not found."));
    lcd.setCursor(0, 1);
    lcd.print(F("Please check wiring."));
    while (1);
  }

  particleSensor.setup();
  // Configure sensor with default settings
  particleSensor.setPulseAmplitudeRed(0x0A);
  // Turn Red LED to low to indicate sensor is running
  particleSensor.setPulseAmplitudeGreen(0);
  // Turn off Green LED

  if (!myDFPlayer.begin(mySoftwareSerial)) {
    // Use softwareSerial to communicate with mp3.
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Unable to begin:"));
    lcd.setCursor(0, 1);
    lcd.print(F("Please check wiring!"));
    lcd.setCursor(0, 2);
    lcd.print(F("Please insert SD!"));
  }
  lcd.setCursor(0, 3);
  lcd.print(F("DFPlayerMini online."));
  myDFPlayer.volume(20);  // Set volume value. From 0 to 30
  delay(2000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("CMU IDeATe PhysComp"));
  lcd.setCursor(0, 1);
  lcd.print(F("Vitals Alarm"));
  lcd.setCursor(0, 2);
  lcd.print(F("Powered by sgeiser"));
  lcd.setCursor(0, 3);
  lcd.print(F("BOOTING DEVICE..."));
  delay(2500);
  myDFPlayer.play(1); // 0001_ps2.mp3
  delay(2500);
  lcd.clear();
  customKey = customKeypad.getKey();
  lcd.setCursor(0, 0);
  lcd.print(F("Input the CPU and"));
  lcd.setCursor(0, 1);
  lcd.print(F("GPU die temps for"));
  lcd.setCursor(0, 2);
  lcd.print(F("your computers (in"));
  lcd.setCursor(0, 3);
  lcd.print(F("degrees Celsius)."));
  delay(5000);
  lcd.clear();
  inputPCVitals();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Input when you went"));
  lcd.setCursor(0, 1);
  lcd.print(F("to sleep and how"));
  lcd.setCursor(0, 2);
  lcd.print(F("long you slept in"));
  lcd.setCursor(0, 3);
  lcd.print(F("24-hour time."));
  delay(5000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("If any value is less"));
  lcd.setCursor(0, 1);
  lcd.print(F("than 10, then prefix"));
  lcd.setCursor(0, 2);
  lcd.print(F("with a zero."));
  delay(5000);
  lcd.clear();
  inputSleepPattern();
}

void loop() {
  customKey = customKeypad.getKey();
  long sum = 0;
  for (int i = 0; i < 10; i++) {
    sensorValue = analogRead(GSR);
    sum += sensorValue;
  }
  gsr_average = sum / 10;
  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue) == true)
  {
    // We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20)
    {
      rates[rateSpot++] = (byte)beatsPerMinute;
      // Store this reading in the array
      rateSpot %= RATE_SIZE; // Wrap variable

      // Take average of readings
      beatAvg = 0;
      for (byte x = 0 ; x < RATE_SIZE ; x++)
        beatAvg += rates[x];
      beatAvg /= RATE_SIZE;
    }
  }
  lcd.setCursor(0, 0);
  lcd.print((String)"Average GSR: " + gsr_average + "    ");
  lcd.setCursor(0, 1);
  lcd.print((String)"Average BPM: " + beatAvg + "    ");
  lcd.setCursor(0, 2);
  lcd.print(F("#: Assess vitals"));
  if (customKey == '#') {
    lcd.clear();
    assess();
  }
}

void inputJarvisCPUTemp() {
  lcd.setCursor(0, 0);
  lcd.print(F("Jarvis CPU Temp: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      temp[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  jarvisCPUDieTemp = atoi(temp);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

void inputJarvisGPUTemp() {
  lcd.setCursor(0, 1);
  lcd.print(F("Jarvis GPU Temp: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      temp[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  jarvisGPUDieTemp = atoi(temp);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

void inputTarsCPUTemp() {
  lcd.setCursor(0, 2);
  lcd.print(F("TARS CPU Temp: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      temp[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  tarsCPUDieTemp = atoi(temp);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

void inputTarsGPUTemp() {
  lcd.setCursor(0, 3);
  lcd.print(F("TARS GPU Temp: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      temp[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  tarsGPUDieTemp = atoi(temp);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

// I am not fond of how I wrote the above 4 functions.
// I could probably condense them into one and save some
// progmem (though I'm not really short on that). The code
// would certainly be cleaner and easier to read, though.

void inputPCVitals() {
  customKey = customKeypad.getKey();
  inputJarvisCPUTemp();
  inputJarvisGPUTemp();
  inputTarsCPUTemp();
  inputTarsGPUTemp();
  lcd.clear();
}

void inputSleepStart() {
  lcd.setCursor(0, 0);
  lcd.print(F("Sleep start: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      sleep[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  sleepStart = atoi(sleep);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

void inputSleepAmount() {
  lcd.setCursor(0, 1);
  lcd.print(F("Sleep amount: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      sleep[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  sleepAmount = atoi(sleep);
  // Convert the char array to int and store it. Note
  // thatatoi is deprecated, am going to change it soon.
  j = 0;
}

// As with the 4 functions that deal with inputting the
// die temps in the PCs, I am not fond of how I wrote
// these two functions. I hope to condense them, too.

void inputSleepPattern() {
  inputSleepStart();
  inputSleepAmount();
  lcd.clear();
}

void assess() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Assessing PC vitals"));
  if ((jarvisCPUDieTemp >= 85 || jarvisGPUDieTemp >= 90) &&
      (tarsCPUDieTemp >= 85 || tarsGPUDieTemp >= 85)) {
    bothPCsBad();
  }
  else if (jarvisCPUDieTemp >= 85 || jarvisGPUDieTemp >= 90) {
    jarvisBad();
  }
  else if (tarsCPUDieTemp >= 85 || tarsGPUDieTemp >= 85) {
    tarsBad();
  }
  else {
    lcd.setCursor(0, 1);
    lcd.print(F("PC vitals OK!"));
    delay(1000);
  }
  lcd.setCursor(0, 2);
  lcd.print(F("Checking your vitals"));
  delay(2500);
  if (gsr_average > 500) {
    lcd.setCursor(0, 3);
    lcd.print("Could not read GSR.");
    delay(2500);
  }
  if (beatAvg <= 40) {
    lcd.setCursor(0, 3);
    lcd.print("Could not get pulse.");
    delay(2500);
  }
  if (gsr_average < 150 || beatAvg >= 130) {
    stressed();
  }
  if (sleepStart <= 6 || (sleepAmount < 7 || sleepAmount > 12)) {
    badSleep();
  }
  if (!humanVitalsGood && !pcVitalsGood) {
    allBad();
  }
  else if (!pcVitalsGood) {
    pcBadHumanGood();
  }
  else if (!humanVitalsGood) {
    pcGoodHumanBad();
  }
  else {
    allGood();
  }
  resetFunc();
}

void badSleep() {
  humanVitalsGood = false;
  lcd.setCursor(0, 3);
  lcd.print(F("Get better sleep!   "));
  delay(5000);
}

void jarvisBad() {
  pcVitalsGood = false;
  lcd.setCursor(0, 1);
  lcd.print(F("Warning on Jarvis!"));
  delay(5000);
}

void tarsBad() {
  pcVitalsGood = false;
  lcd.setCursor(0, 1);
  lcd.print(F("Warning on TARS!"));
  delay(5000);
}

void bothPCsBad() {
  pcVitalsGood = false;
  lcd.setCursor(0, 1);
  lcd.print(F("Warning on BOTH PCs!"));
  delay(5000);
}

void stressed() {
  humanVitalsGood = false;
  lcd.setCursor(0, 3);
  lcd.print(F("You're stressed..."));
  delay(2500);
  lcd.setCursor(0, 3);
  lcd.print(F("Take a break, dude."));
  delay(5000);
}

void pcGoodHumanBad() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Your PCs appear to"));
  lcd.setCursor(0, 1);
  lcd.print(F("be running great..."));
  lcd.setCursor(0, 2);
  lcd.print(F("How about you take"));
  lcd.setCursor(0, 3);
  lcd.print(F("care of yourself???"));
  myDFPlayer.play(4); // 0004_bruh.wav
  delay(5000);
}

void pcBadHumanGood() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Something is up with"));
  lcd.setCursor(0, 1);
  lcd.print(F("at least one of your"));
  lcd.setCursor(0, 2);
  lcd.print(F("PCs. You should prob"));
  lcd.setCursor(0, 3);
  lcd.print(F("check that out..."));
  myDFPlayer.play(2); // 0002_ohno.mp3
  delay(5000);
}

void allBad() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Oh my God, it's all"));
  lcd.setCursor(0, 1);
  lcd.print(F("broken! Seriously,"));
  lcd.setCursor(0, 2);
  lcd.print(F("you need to get on"));
  lcd.setCursor(0, 3);
  lcd.print(F("top of this..."));
  myDFPlayer.play(5); // 0005_battery_low.mp3
  delay(5000);
}

void allGood() {
  lcd.setCursor(0, 3);
  lcd.print(F("Your vitals are OK!"));
  delay(2500);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Nicely done! Your"));
  lcd.setCursor(0, 1);
  lcd.print(F("vitals look alright,"));
  lcd.setCursor(0, 2);
  lcd.print(F("and your systems are"));
  lcd.setCursor(0, 3);
  lcd.print(F("running smoothly!"));
  myDFPlayer.play(3); // 0003_scatman_world.mp3
  delay(13000);
}

 

]]>
B.P.M. (Beats Per Meditation) https://courses.ideate.cmu.edu/62-362/f2020/process-blog-flow/ Wed, 21 Oct 2020 02:57:53 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=9903

William Lamkin

Final Documentation 11-13-20

(1. Me and my sister using the B.P.M. Device)

(2. Another shot of the Arduino, Breadboard, and Ableton Live open on my laptop)

(3. Detail shot of the Arduino and breadboard)

(4. example of use in low-light meditation environment)

(5. Another detail shot, this time with sensors visible for scale)

(6. Short video demonstration)

(7. Long video demonstration)

 

Narrative Description:

B.P.M. consists of two pads, one held by one person between their fingers, and the other held by another between theirs. These pads read their heartbeat and turn it into dance music, making drums and hand clap sounds beat with their heart. How fast their hearts beat also affects other layers of the music as well. They can use this in any circumstance they want. They might want to calm down and meditate to the music in a dark room. They might just want to have fun and share an experience with each other.

 

Progress Images:

(1. A temperature sensor sending data into Arduino. Initially I was planning on using temperature as another metric for getting biometric data into the system, but ultimately, these ended up not being usable, as they are slow to change temperature and require a lot of skin contact to even start reading skin temperature accurately. Perhaps a different kind of temperature sensor designed more specifically for skin would have worked better.)

(2. Another picture of the temperature sensor, and you can see, even with a large amount of skin contact, the temperature reading of the sensor only got up to around 90 degrees Fahrenheit. So, not too accurate for my intentions.

(3. An initial use of the pulse sensors. This is with just one of them so I was able to plug it directly into the Arduino Uno. You can see the LED light very clearly on this one. As well as this I have it connected to a velcro strip on the back. This might have been useful, but I found holding it between two fingers to be more stable.)

(4. A demonstration of the pulse data going into the Serial Plotter in the Arduino IDE. It is very clean in this picture! Which is surprising in hindsight, as the data ended up being a lot noisier.)

(5. An initial test sketch I made in Arduino to send two different values into Max. You can see here I was just testing two random values. The interpretation of the data from serial was the most intimidating aspect of developing this project.)

 

Process Reflection:

In a way, this project fulfills a long-standing desire of mine to learn how to get interesting data from the outside world into Ableton. I’m always looking for new methods of control, and this project gave me insight into how to design new kinds of instruments, for example. Although the idea is simple in concept – take heartbeats and convert them into musical data – this turned out to be a lot more complicated than I initially realized.

In my initial idea of the piece, I was going to use the “Connection Kit” device on Ableton’s website, which is preconfigured to work with Arduino, no extra application of coding needed. This uses the Firmata protocol, and unfortunately, the nature of the pulse sensor is that to get any meaningful data out of it, a lot of signal processing needs to be done. By itself, mapping the pulse data to a musical parameter is an interesting modulation waveform, but I wanted the heartbeat itself, or the BPM. These are things that needed to be calculated and to do that I needed to do a lot of extra coding. Fortunately, I was able to figure out how to just pass the data into Max/MSP, which is designed to work well with MIDI in particular. But before that revelation, there was a lot of unclarity with how to do this. I actually bought an Arduino Leonardo, which I ended up not using, but I had an idea for a while to turn the Arduino into a USB MIDI device to just send notes directly to Ableton that way.

I am also glad I was pushed to make the project more complex, which gave me the idea to make this for two people instead of one. I think the idea of meditating with TWO people, and the intimacy that creates is a lot more interesting than what I would have done with just one person.

 

Code Submission:

Arduino

// B.P.M. (Beats Per Meditation)
// William Lamkin
//
// This takes data from two pulse sensors and sends it
// over serial to be processed in Max MSP.

const int PulseIn1 = A0;
const int PulseIn2 = A1;

long Sig1 = 0;
long Sig2 = 0;

void setup() {
  Serial.begin(9600);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
}

void loop() {
  Sig1 = analogRead(PulseIn1);
  Sig2 = analogRead(PulseIn2);

  Serial.print(min(Sig1, 800));
  Serial.print(" ");
  Serial.print(min(Sig2,800));
  Serial.println();
  delay(50);

}

Compressed Max patch (copy the whole thing to clipboard and in Max, select File>New From Clipboard)

<pre><code>
----------begin_max5_patcher----------
2039.3ocybszaiibD9r8uhND4PBfGG1uHECBBPlrWVfX.CjC4PlAKnHaaycn
XKvGNdxhM+1S+fbr8NTVEspl1CfkgnoT0e0iup5pZN+x4mEsU+fpKh7mI+ax
Ym8Kme1YtKYuvYiu+rnc4OTTm24tsnB8tcpl9nK7+sd0C8tq+wKu9xqH+gOp
x66HWqZIWoJq5y6qzM+QxU4OP9mU21jWSttUWn55pZtknugb8Pcmh7C484ep
4eUUWWkui7Ox28kplO0720M2qZMeaE5l9plA8PGYu69KM2OopoWS1osKl71u
R15DbdSIIutSSJxqKFpy6UcjOd8USq15pFUgdnwsjEiWrYXWUSsp2gO53EqJ
cnRu8m+.kE83cpG5mt03wqtOuu3NCd9oVUQuWUxxtL9BBkYekm3dSR7kwjOa
+D+54mae4hSTgmOTVYT.Cc8FrS5GZaTkDcCoeD7Frag9uaVry.hcAewXeSbp
EuYTmJPF6zAbjAee9WTidCdidmporiXVgj7NxU+3O7ijFcu5RRwc4M2pbuw9
WZTpRU4kjR8v1ZEontp3Kt+n8SZ8mLeujtgVEox3MY+RsNp820pGt8NiiXqE
mj8519YUqI.Uq7zEqVS8ZRoH1qW41esIEY8pEwjhBxNSHZtQuYUKeXRI1Y7q
TNss4NJU2WUbXc5A0kNE8cpuoKc1pSWg9J7S8pPtPdo7aNpnqP+Kev+OhTFS
p5rtRpt6z0kj6yqGTjazsV9MKQmU6jOxv4H21Zz5ypU3.0JLwqf4xE2JDYOI
7UfsVQ27mz2bCwHVU08FkPqdG4u0VNT0nOI5J5xAr2KHcSHYq7gDeJZeqwt9
oHavwMUlvnQH6b+cdBcp1JSNRCTrquSRSPWrlfll3yZE+j.hWstvH1sp1HfK
2zjnusvZy2o5Us+jpI2vm7zE8ygxjpveo9utW4wQTzEjHWrC4yu.epIt2huT
m82CVFac.qXsAqLggGX6pqJW.XYKGrzCB1WBjIYdbQcFVlOOo30V1Ui5+XV9
eeBRWNPCWtI9vTpCIdV8PxL5g3HDw5j26HMcL+zbemGqE801r0kjmQl7HJ4y
fxkS6LBEQryfkDDj7LqFy7qXv1sjLLsaxD5SsazzULRLYyZEIJ8aAf4MoAJR
7QuyXndmIIKu7.uAax6TrhoIR3uU4DYLDRSLtEhuyt86olu44MYrYTBh.Pax
jtvuw2fLWisbMvNjxnvYFYYBuq6Z5yxV+Ranu27YkgHkw6FeVYZT3LiH4yNO
Ja8atlMKRimAoIXZGS137PEab6xRRCHBofQnDUO0MdWTeKDBJBAaCEavM+Q5
irLmJ.WDwpH6MpX.FKMfDqa2uiPY7KYva1wbDtgXuU1FfHm.NWhqa7+slXJd
uPQnfSpPeEztfvYlmP5zRrrcnuW2.0aVhYSA.3Im9XO9XhoWQcCleso3+AF8
GnY07CgvN2nybFTyOU1fiOezIEPogOUJ3DMhTbKIhiGM7ALnuTdl43mW9VKY
wtUdh2iTxCHLnfggb4yQAQTbnzD7LRxg501LbkBZ.JbSx88oJKLHLYYHLN.E
tgCBOfm3zPVKftOC9xG6dB2aox7vYyJlQispYzFoNj9Xu.kRSUCu5DFegUm3
ms4Q7KoYijJo9llFjlb6lsMXyb5BScOkvFPd6IzFm32mXP3Y.yvvPciSLeVZ
Q1FLl117Vx+pcF6fg2R6QCPqmH0uegr0j6IdM4d17Ni5gtIDTOou2XdnIgi4
I88DwCE0Ran72YDObj4cRQj14P1N24VAboonNwF534cik36x+Z1xs2pYYD9V
tYmL6I0wMdR.Fww6rNtwQuiaS37Mnia7UsFgwFz7toiarjf1wsQ3hSG2NTbq
jlANEJpcaiMVWPPYkjOaM+xnC0gVsI0x4H3rvUevPy97huPNT6clqXuktcZn
E5k4OFf9CyRZH.q8r7180ca00PAKtSnyiPu4LMHsFtpWmC97vgc0dYAeJ4lD
k21pG1aOjbvOlNnObJOPkNezMgoE+pZ6Cf.kufSPlbg4Ql1q4B1yIymEI8z5
jdu91aqAu4RAhEGbLWX+A0weJHBSoAF.zpIKnWPQX2d1wG+KeByjv375dRAJ
IYIwvSq75xp7xNsNyIMwUpW57oTbed2FM9MOpgtuO2gO5YJfN8PawzhXLEgI
84zpnT00W03dzBex8XyYRNjNFpfrOGBGUR1C47oKIA.IkjfgjXqFlhgHITvD
.AsAA4XO82GGQBLjzl0xevdnSOtjvHZJAjOtDCIwgXmhwPRPhllbaNMIAARH
HGIDebIJRBRX6jB9zjDDkm.CNOIjnIAFdd1iMDgdL6zyof0s1GPDa0.qjnoe
uniOMQCIdShAak8D1cTPJvHxVBIOoDEuSHAbHIG1JHGADtpDLxGKfjOVhAWk
.BqHGCFDAj7wX31I.EzhQnzDG9Kq7PwLEuZRBffvPNbHII4X3OvAQshBlfXk
XXPEAohSZRHxFCRxAIYLCB8q8jC8VI5zPnuYPHnYXTBhyw7XwI7f3TAWz3aZ
g3PyvfcfAIuj.C1AFDdHAFa8ksR4zoPJHhgQYJTH4knXzdGJDOOdHB2nP1Q0
ugyaMEscbr3GoCReamua.DMjJR+NWJemqy2u+dUa23M6DQzt7eV6VVatv81p
F+acscOpUce0z869OUhn71h6p5UE8CstkUzCiOZ6Q6zF.1LTMZYMfyHR2..Z
x2o51m6wgaNAm+qm++MzFSQJ
-----------end_max5_patcher-----------
</code></pre>

 

 

 

——————————————————————————————–

 

Google Slides Presentation! 11-9-20

https://docs.google.com/presentation/d/1-cszJymatEnvi5NvLV3GBG5QRJxCWZodbNqJEoCUgXI/edit?usp=sharing

 

Additional Documentation Photos! 11-4-20

 

90% Complete Critique 11-2-20

video: https://youtu.be/9RfR5KQFVY8

Over the weekend I was able to successfully complete the project and record a “performance”/video documentation of a use case. This required a successful integration between the Arduino and Max/MSP, which all of the signal processing was done in. A Max/MSP patch I created generated midi notes and cc messages that were then sent into Ableton Live to control the instruments in that program. The result of which is a texture reminiscent of trance music.

Pulse 1 triggers a kick drum, and its calculated BPM controls a low-pass filter cutoff on the synth pad.

Pulse 2 triggers a handclap, and its calculated BPM controls the length of sustain on the sequenced/arpeggiated track.

The heartbeat sensors ended up being finicky in that they didn’t allow a user to move their hand at all, almost necessitating extreme stillness. Thankfully this benefitted the goal of the project to encourage meditation. However, because of this, the other sensor element of this project, the temperature sensors, proved unfeasible. The temp. sensors required a lot of skin contact to raise the reading higher. Because of this, the hand positions required for both sensors were incompatible and the temp. sensors ended up being dropped.

Draft statement: B.P.M. (Beats Per Meditation) is a device for use with Ableton Live and Max/MSP that uses two pulse sensors connected to an Arduino. These pulse sensors use the real heartbeat of two people to trigger percussion in Ableton Live, while also calculating their BPM to modify other sound parameters, like the brightness of a synth pad. The resulting music is a kind of a trance music, generated by the body. This device asks the users to focus and meditate on their body through the sound, and share that experience with another person. Hopefully, they will be able to calm down from whatever they are experiencing and have some new insight about themselves.

As an electronic musician, I have always been interested in incorporating new modes of interaction with music. Biometrics seemed like an obvious avenue for exploration, and something like dance music, which already has influence from the beat of a heart, fit the bill perfectly. As well as this, I thought it would be extremely useful to integrate the new technology I had been learning with Arduino, with software tools that I was already familiar with. This is the result of these experiments.

 

Process Blog Check-In 10-28-20

Received the sensors and have got them working!

This is the TMP36, a temperature sensor. I used a library I found in the library manager titled “TMP36” which does the conversion of the signal into fahrenheit/celsius data. It does have some behavioral quirks which I will have to work around, probably the main one being that the it takes a while to change temperature! So I will have to do more testing.

This is the Pulse Sensor, which had a library provided by the developer. The main goal of this was setup which I am glad to have gotten working. Here is a picture of my pulse being displayed on a serial plotter using one of the example patches in the library.

Next steps are to now integrate this into Ableton!

 

Proof of Concept Tech Demo  10-26-20

Here is a video link to a test of the connection between Arduino and Ableton Live using the Arduino Max for Live device from the suite called Connection Kit. https://www.youtube.com/watch?v=EDeQQuitjR8&feature=share

I am awaiting pulse sensors and temperature sensors, which are scheduled to arrive early this week. In their absence, I used a pushbutton and potentiometer as inputs, and demonstrated those inputs controlling parameters in Ableton. Hopefully this will be all I need, but once I start using the new sensors, this might necessitate getting some other parts as well, or adjustments to the code.

Bill of materials as of right now:

Laptop with speakers (have)

Ableton Live (have)

Arduino Uno (have)

2x heart sensors (on their way)

2x temp. sensors (on their way)

 

Group Check-In 10-21-20
BPM (Beats per Meditation) is a device meant to be shared between two people. The two users are asked to sit down and meditate to ambient/trance music being played in Ableton Live. Both users are listening to this via separate headphones to make the experience more immersive. Each user also has their hand connected to a pulse sensor, and a thermometer that connect to an Arduino. This Arduino is hooked up to Ableton Live, and this is how the biometric data is sent into Ableton Live. Each user’s biometric data corresponds to different parameters in the music, which aren’t explicit immediately, allowing for exploration of the the inputs. Each user’s pulse is very important – one person’s heart controls a kick drum, and the other triggers a snare (or hand clap). As the users’ heart rates calm down the music becomes lusher. Other relaxing elements are placed nearby to further accompany this, such as a hand warmer. This, when used, could also effect the temperature input if a user warms their hands before touching the thermometer pad. The goal of this shared experience is to provide a way for two people to meditate together and to use music as a medium to connect them. I was drawn to make this project because I’m very interested in using biometric data like the heart in music, so this will be a great opportunity to try something like that out and to learn more how to use the Arduino.
Bill of materials:
  • Arduino (already have)
  • Computer/speakers (already have)
  • Ableton Live (already have) + Connection Kit (free download)
    • Or Max/MSP (already have)
  • Two pulse sensors such as this heart rate sensor
  • Two temperature sensors such as this LilyPad Temperature Sensor

 

]]>