Documentation – 62-362 Fall 2021 https://courses.ideate.cmu.edu/62-362/f2021 Electronic Logics && Creative Practice Sat, 09 Oct 2021 02:36:44 +0000 en-US hourly 1 https://wordpress.org/?v=5.7.11 Cov-Aid Belt: Tushhar Saha https://courses.ideate.cmu.edu/62-362/f2021/cov-aid-belt-tushhar-saha/ Sat, 09 Oct 2021 02:36:44 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11116 Description

The Cov-Aid belt is a belt that helps you stay safe and battle coronavirus. It has 3 switches that help you select your statuses and 1 ultrasonic distance sensor that detects people within 6 feet in front of you. For this belt, the range is smaller than 6 feet due to limitations of the sensor. The 3 statuses are if you are masked or not, vaccinated or not, and if you are indoors or outdoors. Based on the combination of these switches and the sensor, the belt tells you if you are safe or unsafe. There are 4 outputs in total. One LED tells you if you are safe. One LED tells you if you are unsafe. The other two outputs are the buzzer and the pancake vibration motor. They work together to alert the person wearing the belt if they are inside and someone is less than 6 feet close to them. The safe LED is only on when the other outputs are off.

The belt is made up of laser-cut wood. It is cut in such a way that it can be bent and stretched. It is wide enough to hold all the components, and it is long enough to fit almost any person. The belt can be fastened by tying the connected rope.

In-Progress Media

Initial model of how I wanted my belt to look like

Soldering all the components so they can be used out of the breadboard

Testing the circuit with soldered components to make sure the logic is working

Testing components with batteries after initial mounting

Mounting everything on the belt with double sided tape and glue gun

Process Reflection

What worked well?

The flexibility of the belt seemed to have helped a lot in fastening it, as it would not only bend really well but also stretch enough to tighten around the waist. The belt was also hard at the same time allowing easy mounting of components. On top of that, bendable wood has always been a fascinating thing to me so I was glad I got to use it.

When I first tried it on, I was afraid that the components wouldn’t stay vertical. However, it seemed to be staying on pretty strong. It was even more apparent when I was trying to dismount some of the things on the belt. The reason I wasn’t too confident about it staying on was in fact not the mounting mechanism but the belt. I shall talk more about this in the next section.

Thanks to the suggestion in the in-progress critique, I made sure the switches and LEDs were facing upwards by mounting them on the board on top of the belt. This allowed the user to interact with the switches and read the LEDs properly simply by looking down.

What could have been better?

I definitely believe that the belt could have looked better. While it did give off a ‘cyborg’ vibe, the inner workings and the electronics were something I was hoping I could hide. It was an issue I ran into after mounting the components. I realized that the belt was too heavy. While the user would have been able to bear more weight, I cannot say the same about the belt. The laser-cut design that caused the belt to bend also made it weak in the vertical direction. If I added more material to hide the circuits, the middle part with the components would put too much weight resulting in either a break in the belt or a very uncomfortable and unstable belt.

The next thing I would have hoped to improve was the mounting of the components on the wooden board and how it looked. This had to be done by drilling holes instead of using the laser cutters as the laser cutters were closed on Sunday. In hindsight, I should have planned this step better to have what I needed laser cut beforehand.

Furthermore, I would have also preferred to use a different sensor with not only a larger range but also a way to detect if the object detected is a person and not a wall. This was an issue that arose at one of the earlier discussions. While it wasn’t on top of my priority list to solve this issue, I would have still preferred if I could come up with something that allowed me to differentiate an object and human being with ease.

Finally, I did gain a lot of insight into improvements I could have made in the critiques we had. Two major points were the material I could have used and another form of the kit I could have used. The first critique made me understand that I can deal with some of the weight problems I was having by using a fanny pack instead of a belt. It did seem like a good way to make it look visually better as well. The fastening mechanism would also be more secure this way. The second critique was that instead of using it like a belt, I could use it like a sash. This would definitely make it easier to deal with in terms of weight. Not only would it not feel as heavy, the weight would also be distributed horizontally.

Learning

I definitely enjoyed utilizing the maker cards with a lot of different output, input, power and control components. They helped me widen my outputs for this project, and I know that I will be using them again in the future. I am glad I was also able to implement transistors for driving components that couldn’t be driven by logic circuits. This was my first time using transistors in an IDEATE project, and I am glad I have this in my arsenal for the next upcoming projects. Lastly, the logic gates were something I was excited playing with. While I knew how they worked, I still felt like a kid trying out new toys when working with actual gate components. Learning with them made me realize how many things there are in this world that can be split into simple binary.

Pictures and Video of Cov-Aid in action

Presentation with the belt

People interacting with the logic of the belt

Fastening the belt on a volunteer

People testing the distance sensor and the different statuses

Belt on a different person to show it works on multiple waist sizes

Belt in action

Code for Ultrasonic Sensor

/*
*Cov-Aid Belt
*This code is for the ultrasonic sensor. It measures the distance of any object from the sensor 
*and compares it to a threshold. If it's less than the threshold, it outputs a HIGH to SEN_PIN 
*and LOW otherwise.
*/
#include <NewPing.h>

/* Part of the code taken from https://nsfsmartmakerspaces.github.io/physcomp/parts/0572/ */

const int TRIGGER_PIN = 11; // Digital output pin for chirp trigger
const int ECHO_PIN = 12;    // Digital input pin for echo reply
const int SEN_PIN = 13;
// Maximum distance we want to ping for (in centimeters).
// Typical maximum sensor distance is rated to 100cm.
const int MAX_DIST = 100;

// Variable to keep track of the current proximity
int currentDist;
int outSen;

// Setup the ultrasonic ranger with the correct pins and
// maximum distance
NewPing mySonar(TRIGGER_PIN, ECHO_PIN, MAX_DIST);

void setup() {
  pinMode(SEN_PIN, OUTPUT);
  // Setup serial port to send the current proximity back to the computer
  Serial.begin(9600);
}

void loop() {
  // Get the ultrasonic ranger distance
  currentDist = mySonar.ping_cm();
  // Send HIGH when object less than 10cm, LOW otherwise
  // currentDist == 0 implies that no object is detected
  if (currentDist<10 && currentDist!=0) {
    digitalWrite(SEN_PIN, HIGH);
    outSen = 1;
  } else {
    digitalWrite(SEN_PIN, LOW);
    outSen = 0;
  }
  // Send the data over serial
  Serial.print("Ping (cm): ");
  Serial.println(currentDist); // Send ping, get distance in cm and print result (0 = outside set distance range)
  Serial.println(outSen);
  // Delay to not send messages too fast.
  delay(100);
}

 

]]>
are you ok? https://courses.ideate.cmu.edu/62-362/f2021/are-you-ok/ Fri, 08 Oct 2021 20:42:16 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11365

Background

“are you ok?” is a satirical implementation of a mental health support kiosk from the 70s. The inspiration for this project came from the feelings of frustration and alienation that come from dismissive, canned responses to serious mental health issues. The bingo card above would feel a bit too familiar to anyone who’s been through depression, anxiety, or other mental health issues. People often don’t know how to provide good support and end up gaslighting, diminishing your experiences, or providing dismissive advice like “take a deep breath.”

Good mental health support requires listening, validating, and responding thoughtfully. The biggest requirement for being a good supporter is simply empathy – you don’t need to be a therapist for this. The kiosk breaks all the rules, because it

  • Doesn’t pretend to care about you or your emotions (it’s a robot…)
  • Only provides canned responses
  • The only input is a binary Y/N, you can’t open up to this thing
  • It seems to solely attempt to diagnose you and move on
  • It’s impersonal
  • It only acts logically
  • They might be in public locations, or in groups. There’s no privacy since it speaks aloud.
  • It’s dismissive, ending each action with a “we hope you are feeling better.”
  • There’s no debating it. Each “diagnosis” is authoritative.

The design of the kiosk is hostile, but very simple and “corporate.” The voice is distorted and robotic, and the CRT TV monitor is staticy and industrial, with small white text on a black screen.

Narrative and Interaction

The interaction is simple. In its idle state, the kiosk simply displays “are you ok?” with blinking options for “yes” and “no.” Answering yes will terminate the session, but answering no will kick off a series of prompts that attempt to provide mental health support.

Prompts are printed to the monitor sphere on the top of the kiosk in small white lettering, and read aloud by a robotic voice using the TV speakers. After each prompt, the user can select “yes” or “no” again to continue the interaction. Eventually, the user might end up on a prompt that is simply advice such as “Try getting more sleep” or “consider applying to graduate school” or “try chamomile tea.” Answering yes or no here will terminate the session, and display “Thank you for using this mental health support kiosk. We hope you are feeling better.”

The kiosk will reset if inactive for 15 seconds. While it’s running, the prompts are printed on my laptop so I can see what people are selecting even if I’m not within earshot.

The video below shows an example interaction, and the video at the top shows a longer series of prompts.

Photos and Display

I showed this in Gates and outside Purnell. Both times, we setup the kiosk in the middle of a walking area where we would get a fair bit of foot traffic, then sat nearby watching people interact with it. I printed a fake museum card to attach to the kiosk (see above) with a long-winded fake backstory that described it as a restored object from the 1970s.

Interestingly, demographic differences were very apparent in how people interacted with the kiosk. Outside Purnell, we had a lot more students laughing at the humor of the project, and most people engaged with the “depression” tree the most. At one point, a group of drama students went and grabbed their friends to show them the kiosk. A few parents used it! When we showed it in Gates, most people engaged primarily with the “stress” tree, and seemed more confused and frustrated by some of the prompts, but still usually ended up laughing at the end.

Most people were a bit nervous at first to answer it, and would kind of tentatively press the buttons at arms length. After the first prompt though, they’d get a bit more casual and start to actually engage fairly deeply with it. I was worried that people would be uncomfortable using it in public, but it turned out that a lot of people were very willing to engage with it either alone or with their friends. People would hang out and watch other people use it while reading the fake “artists statement” on the side, and try to explain their theories about where it came from.

The responses deeper into the tree tended to get a lot more abusive and emotionally painful to interact with. This was the “gaslighting” part of the tree, that asked users if they were “sure they were depressed,” making claims that they were “didn’t seem depressed,” or were “bringing down the vibe,” or that they were “using this kiosk to get attention.” It was interesting seeing the groups move from lighthearted responses to the more aggressive ones, since they would transition between laughter and clearly being uncomfortable. I think it was worth including that part of the tree, but it was really painful to watch random people engage with it.

 

Process

The construction is simple. It’s an old television sitting on a white plinth with a pair of buttons. The electronics are a raspberry pi running processing and a TV modulator box to convert the analog video into a VHF TV signal.

Reflection

This project ended up being a lot of fun to work on, especially writing the prompts and seeing people use it. The easiest parts were definitely writing the prompt code and building the plinths. Getting the code to run on a raspberry pi turned out to be a bit of a challenge, and I had a lot of issues with Processing’s gpio library on the raspi 4, and ultimately had to add a retry mechanism for it to reliably start. Setting up the TV and modulator was a bit of trial and error since there’s little documentation and knowledge about these TVs floating around, and at one point I needed to switch from UHF modulation to VHF to get a good image.

Thematically, I think the aesthetic went pretty well and didn’t take a lot of work to pull off. On the other hand, coming up with prompts turned out to be fairly difficult. I did a fair bit of research into mental health support techniques, modern therapy methods, and talked to a lot of my friends and family about their personal experiences. At some points I struggled a lot to balance between keeping it lighthearted versus putting in more emotionally painful prompts. There was a huge amount of ground I didn’t feel like I had time to cover appropriately that I left out.

I think if I did it again I would spend a bit more time planning the prompts themselves from an interactivity point of view: when I showed the piece, most people didn’t break past the first couple levels of input. I’d guess that at least 3/4 of the tree wasn’t explored by anyone using it in passing. I think setting it up more permanently or in a gallery would invite longer interactions, but people using it between classes or with their friends meant that a lot of the users didn’t want to spend much time diving into it.

A few people at the critique noted that logging responses would be cool, and I totally agree. It would have been really interesting to set this up for a couple days in different spots on campus and see how people interact with it.

Code

/* Are You Ok?
 * Tom Scherlis 2021
 *
 * Are you ok is a satirical mental health support kiosk developed
 * for the course Electronic Logics && Creative Practice at 
 * Carnegie Mellon University. This software was written in Processing
 * and is intended to run on a raspberry pi.
 *
 * requirements: the command 'espeak' should be installed on the system.
 *
 * pin mappings are below. LEDs should be active high, and buttons should
 * be active low.
 *
 * If running on hardware, set hardware = true, otherwise the system will
 * not initialize gpios and will run in a windowed mode using keyboard
 * y/n inputs.
 *
 * Reminders: Disable screen blanking if you intend to run this kiosk for
 * more than a couple minutes at a time.
 * 
 */

import processing.io.*;

// Pin mappings:
int noLED = 27;
int yesLED = 22;
int noButton = 4;
int yesButton = 5;

boolean hardware = true;

// Prompts form a binary tree.
class Prompt {
  String value;
  Prompt yes;
  Prompt no;

  Prompt(String value, Prompt yes, Prompt no) {
    this.value = value;
    this.yes = yes;
    this.no = no;
  }
}

PFont f;

boolean speak;          // This loop we should speak and log the prompt. (Prompt changed)
int promptStartTime;    // The time that the current prompt started
int lastPromptEndTime;  // The time that the current prompt ended and user input started
Prompt activePrompt;    // Current active Prompt
Prompt rootPrompt;      // Root Prompt ("are you ok?")

void setup() {
  size(640, 480);  // use "size" for windowed mode, "fullscreen" for fullscreen mode on the pi.
  //fullScreen();
  frameRate(40);
  
  // Create the font
  //printArray(PFont.list());
  f = createFont("courier", 20);
  textFont(f);
  textAlign(CENTER, CENTER);
  
  // Sometimes gpio configuration fails on the first try, so loop.
  boolean configured = false;
  int tries = 0;
  while (!configured && tries < 50 && hardware) {
    try {
      GPIO.pinMode(noLED, GPIO.OUTPUT);
      GPIO.pinMode(yesLED, GPIO.OUTPUT);
      GPIO.pinMode(noButton, GPIO.INPUT_PULLUP);
      GPIO.pinMode(yesButton, GPIO.INPUT_PULLUP);
      configured = true;
      tries++;
    }
    catch(Exception e) {
    }
  }
  if (configured || !hardware) {
    print("Successfully configured GPIO!");
  } else {
    print("Failed to configure GPIO!");
    return;
  }
  
  // Declare the prompt tree
  promptStartTime = millis();
  rootPrompt = new Prompt("are you ok?",
    null,
    new Prompt("have you been feeling depressed recently?", 
      new Prompt("I'm sorry to hear that.\nHave you been getting enough sleep?",
        new Prompt("Have you been exercising regularly?",
          new Prompt("Have you been eating a nutritionally rich diet?\nStudies show that processed foods\nhave a negative effect on mental health!",
            new Prompt("Try high-fiving a nearbye friend!\n... ... ... ... ... ... .... \nAre you still feeling depressed?",
              new Prompt("Have you experienced the death of\na family member recently?",
                new Prompt("I'm sorry for your loss.", null, null),
                new Prompt("Are you sure you are depressed?",
                  new Prompt("A negative attitude does not help.\nYou're really bringing down the vibe.",
                    new Prompt("Have you considered that many people\nhave it worse than you?",
                      new Prompt("Hmm.. Consider reaching out to\nCounseling and Psychological Services (CAPS)™\nor an online therapy resource such as TalkSpace™.\nWith Talkspace™’s Guaranteed Response Time™, you’ll\nknow when to expect your daily response from\nyour counselor, allowing you to get the most\nout of your counseling.", null, null), 
                      new Prompt("That's awfully selfish of you.", null, null)
                      ),
                    new Prompt("You don't seem very depressed.\nAre you using this kiosk to get attention?",
                      new Prompt("I hope you find the help you need.", null, null),
                      new Prompt("Hmm.. Consider reaching out to\nCounseling and Psychological Services (CAPS)™\nor an online therapy resource such as TalkSpace™.\nWith Talkspace™’s Guaranteed Response Time™, you’ll\nknow when to expect your daily response from\nyour counselor, allowing you to get the most\nout of your counseling.", null, null) 
                      )
                    ),
                  null
                  )
                ),
              null
              ),
            new Prompt("Try eating nutritionally rich meals, like those\nsold at Nourish™ in the Cohen University Center,\nor by using a service such as HelloFresh™!", null, null)
            ),
          new Prompt("Try exercising.", null, null)
          ),
        new Prompt("Try getting more sleep.", null, null)),
      new Prompt("Are you feeling anxious?",
        new Prompt("Try taking a deep breath!\n... ... ... ... ... ...\nAre you still feeling anxious?",
          new Prompt("Do you think your anxiety is due to an\nimpending climate disaster?",
            new Prompt("Try taking pleasure in the little things\nand avoid thinking about the future.\nGreen energy companies such as Exxonmobil™\nhave their best scientists on it!", null, null),
            new Prompt("Do you think your anxiety is due to an\nongoing global pandemic?",
              new Prompt("Remember that many people lost loved ones,\ncareers, and much more. Instead of\ncomplaining, be grateful that you are in such\na good position yourself.", null, null),
              new Prompt("Are you worried about your future?",
                new Prompt("Are you struggling to find a job?", 
                  new Prompt("Did you decide to enter a field without\na stable job market?",
                    new Prompt("Consider attending an MBA program\nsuch as Carnegie Mellon University's\nTepper School of Business™!", null, null),
                    new Prompt("Consider contacting the Career and Professional\nDevelopment Center for help updating your résumé!", null, null)
                    ),
                  new Prompt("Are you struggling to find a job that you like?",
                    new Prompt("Are you worried that a career in your field\nwon't be emotionally fulfilling",
                      new Prompt("Does you career pay well?",
                        new Prompt("I don't see what you have to complain about then.", null, null),
                        new Prompt("Neither do I.\nMany people do not like their jobs.\n", null, null)
                        ),
                      new Prompt("Have you considered graduate school?\nYou wouldn't want to waste your potential...", 
                        new Prompt("Do it.", null, null),
                        new Prompt("Consider disconnecting from the world\nto join a commune!", null, null)
                        )
                      ),
                    new Prompt("That's good. Things could be much worse.\nAre you sure you are worried?",
                      new Prompt("I'm sorry to hear that. Please reach out if there's\nanything I can do to help.", null, null), 
                      null)
                    )
                  ),
                new Prompt("Congrats!",
                  null,
                  new Prompt("No? You should feel lucky.\n78% of users are worried about their future.\nI'm worried about my future.", null, null)
                  )
                )
              )
            ),
          null
          ),
        new Prompt("Are you feeling stressed?",
          new Prompt("Are you stressed due to coursework or an\nupcoming exam?",
            new Prompt("Consider transferring into an easier\nprogram or taking on less work.\nSome students simply aren't cut out for the\ndifficulty of Carnegie Mellon, especially\nComputer Science or Engineering!\nIs transferring an option for you?",
              new Prompt("That sounds like a good solution.", null, null),
              new Prompt("Try reaching out to your professors for assistance.\nYour mental health is more important than\nacademics. Your professors should understand\nthat your mental health takes priority\nover your career success.\nAdditionally, avoid drugs and alcohol.", null, null)
              ),
            new Prompt("Are you stressed due to a relationship\n or a friend?",
              new Prompt("Try cutting out the harmful or sad people\nin your life. That worked well for me.", null, null),
              new Prompt("Have you tried chamomile tea?\n",
                new Prompt("Have you tried meditation?",
                  null,
                  new Prompt("Try meditating now\n... ... ...\n... ... ...\nAre you still feeling stressed?",
                    new Prompt("Consider downloading Headspace™\nto track and optimize your meditation routine.\n Headspace™ lets you learn to manage feelings and\nthoughts with the lifelong skill of everyday\nmindfulness, any time of the day.", null, null),
                    null
                    )
                  ),
                new Prompt("Try chamomile tea!\nIt is very calming.", null, null)
                )
              )
            ),
          new Prompt("Do you have persistent existential dread?",
            new Prompt("Gates 3rd floor is an\nexistential-dread-free zone. Consider studying\nsomewhere else.\nPlease reach out to me if you need anything.", null, null),
            new Prompt("Are you sure you are not ok?",
              new Prompt("Most Carnegie Mellon University Students\nare ok.\n Maybe you are overthinking things.", null, null),
              null
              )
            )
          )
        )
      )
    );
  
  
//new Prompt(Is capitalism not working for you?", 
//  new Prompt("Consider working harder.\nStop complaining and adopt a 'Winner Attitude.'\nRand Paul (www.randpaul.com) and Turning\nPoint USA™ both have excellent\nresources for learning about the benefits\nof capitalism and why socialism is\na failed ideology.", null, null),
//  null
//  ),
                
  activePrompt = rootPrompt;
  speak = true;
  noCursor();
}

void draw() {
  background(0);
  
  boolean pressingYes = (keyPressed && key == 'y') || (hardware && GPIO.digitalRead(yesButton) == GPIO.LOW);
  boolean pressingNo = (keyPressed && key == 'n') || (hardware && GPIO.digitalRead(noButton) == GPIO.LOW);
  int now = millis();
  
  String promptString = "";
  if (activePrompt != null) {
    promptString = activePrompt.value;
  } else {
    promptString = "Thank you for using this mental health\nsupport kiosk!\nWe hope you are feeling better.";
  }
  
  if (speak) {
    //launch("/bin/bash", "-c", "'", "espeak", "\"" + promptString + "\"", "-p", "0", "'");
    //try {
    //  Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "'", "espeak", "\"" + promptString + "\"", "-p", "0", "'"});
    //}
    //catch(Exception e) {
    //  print("Failed to use espeak!\n");
    //}
    print("\n->",promptString);
    ProcessBuilder pb = new ProcessBuilder("espeak", "-p", "0", "\"" + promptString + "\"");
    try {
      pb.start();
    } catch(Exception e) {
      print("Failed!");
    }
    //print("\n\n/bin/bash", "-c", "'", "espeak", "\"" + promptString + "\"", "-p", "0", "'");
    speak = false;
  }
  
  boolean[] questionStatus = drawTextScrollQuestion(promptString, now - promptStartTime, 1000, 50, activePrompt != null); //5 for testing, otherwise 50.
  //drawTextScrollQuestion("Have you been feeling depressed recently?", now - START, 3000);
  boolean promptDone = questionStatus[0];
  boolean lightsOn = questionStatus[1];
  
  if (!promptDone) {
    lastPromptEndTime = millis();
  }
  if (millis() - lastPromptEndTime > 15000 && activePrompt != rootPrompt) {
    activePrompt = rootPrompt;
    speak = true;
    promptStartTime = millis();
    print(" reset\n-----------------------");
  }
  
  if (activePrompt == null && promptDone && millis() - lastPromptEndTime > 2000) {
    activePrompt = rootPrompt;
    speak = true;
    promptStartTime = millis();
    print(" end\n-------------------------");
  }

  if (activePrompt != null && promptDone && pressingYes) {
    activePrompt = activePrompt.yes;
    speak = true;
    promptStartTime = millis();
    print(" yes");
  }
  if (activePrompt != null && promptDone && pressingNo) {
    activePrompt = activePrompt.no;
    speak = true;
    promptStartTime = millis();
    print(" no");
  }
  
  if (hardware) {
    if (lightsOn) {
      GPIO.digitalWrite(noLED, true);
      GPIO.digitalWrite(yesLED, true);
    } else {
      GPIO.digitalWrite(noLED, false);
      GPIO.digitalWrite(yesLED, false);
    }
  }
}

// Returns: {done prompting, blink state for buttons}
boolean[] drawTextScrollQuestion(String text, int elapsed, int duration, int minDelay, boolean getInput) {
  boolean[] res = {false, false};
  if (elapsed < 0) {
    return res;
  }
  int nchars = text.length();
  int nToDraw = min(nchars * elapsed / max(duration, nchars * minDelay), nchars);
  String prompt_so_far = text.substring(0, nToDraw);
  String todraw = prompt_so_far;
  if (nchars == nToDraw) {
    res[0] = true;
    if (((elapsed - duration) / 1000) % 2 == 1 && getInput) {
      text("NO                      YES", width/2, height*5/6);
      res[1] = true;
    } 
  }
  text(todraw, width/2, height/3);
  return res;
}

 

]]>
RoboVend https://courses.ideate.cmu.edu/62-362/f2021/robovend-andy-kong/ Fri, 08 Oct 2021 19:32:08 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11295

Child Description

This is a skinny black box about a foot tall, with the same shape as a desktop computer. It has a toothy gap down the left side, and is cut out of a glossy, hard material. The top face has a series of copper pads in the shape of a palm and 5 fingers, with green LEDs above each finger. There’s also a gap in the top-center of the top face, through which a snack on wax paper peeks through.

The hand shape of the top invites you to position your hand over it,  and the LEDs light up as each “finger” is touched. Multiple lights can be on at the same time. When a specific set of pads are touched simultaneously, fulfilling a code, the LEDs glow sequentially and a small beep sound is emitted from the interior. Then, a motor whirrs and fruit-by-the-foot is dispensed from the top gap.

Reflection

My project had too much thinking in it. Mainly, I remember spending a lot of time agonizing about what a dominant robot would look like so I could design the vending machine around it, as if I were making a specification for it. As an exploratory design, I should have spent less time nailing down what the actual design would be and instead spent more time iterating over a few designs, and seeing what made sense in the context of a machine user. In the future I’d like to make things that require less thinking, which are more hands-on  enjoyable projects. I don’t know how they tricked me, a hands-on pesron, into thinking so much without doing.

As a rather absent-minded person, I forget a lot of relevant things I’ve learned over the years. One is that physical construction is hard and takes time, even if you know exactly what you’re doing. Mistakes take half an hour to correct, which isn’t the case for electronics or programming. I expected the electronics to go quickly, and I finished those in about 2 hours. But when it came to the wheel that actually dispensed the fruit-by-the-foot, the mount for the motor and fruit roll took me way longer to think of and build. By the time I finished adjusting the gap between the motor and the fruit roll, the plastic gears in the gearbox had clean stripped off, and the wheel no longer worked under any load. Unfortunately, I had hot-glued everything into place because I expected it to work, which turned out to be a terrible decision since I couldn’t get in and replace the gearbox. I forget this stuff when I haven’t been building in a while, but I have been reminded. I also re-learned how to use the laser cutters, which is great! Can’t wait to make more enclosures!

I spoke with Erin a bit towards the beginning of the project about how I felt like anything I could make was futile. It would all end up in the landfill, from nice clean parts that anyone could use, to hole-ridden sheets of acrylic that would be left for the moss to decay in 4 billion years. Erin said futility and uselessness were fine byproducts of the system we’d build since many of the parts would be recycled, which I had and have trouble agreeing with. In the future I will try to build more practical things, since many such projects I think of can fit y’all’s descriptions, be artistic, and be useful. Obviously that is hard, but we are practicing so I should feel less bad if I fail. And I won’t have to force myself to work on them, which is a major incentive.

In-Process Photos

Box prototype for holding reels and dispensing their contents

Box prototype for holding reels and dispensing their contents. Maquette in the back

First design of dots dispenser. I tested on string, but that didn't work really well so I switched it to candy

First design of dots dispenser. I tested on string, but that didn’t work really well so I switched it to candy

Mounted dots dispenser, loaded up with candy

Mounted dots dispenser, loaded up with candy

Showing 2nd iteration of the dots dispenser

2nd iteration of the dots dispenser. The dots go between the roller and the cardboard

Adding a nub to the wheel allowed better grip to things on the track, so much so that I could pull the flat wrapping of the candy. This also informed my decision to add a track in the final version.

Adding a nub to the wheel allowed better grip to things on the track, so much so that I could pull the flat wrapping of the candy. This also informed my decision to add a track in the final version.

Finished Photos

Part of the mythos of the Robovend is its robotic creator, who wanted a vending machine that both humans and factory floor robots could benefit from

Part of the mythos of the Robovend is its robotic creator, who wanted a vending machine that both humans and factory floor robots could benefit from

Wiring of the LEDs and touch responsive pads. The pads were connected to the Arduino through a small hole in the top acrylic panel

Wiring of the LEDs and touch responsive pads. The pads were connected to the Arduino through a small hole in the top acrylic panel

The fruit by the foot dispensing mechanism. This final mechanism worked until the gearbox broke itself

The fruit by the foot dispensing mechanism. This final mechanism worked until the gearbox broke itself

Robovend's pads are all touch responsive

Robovend’s pads are all touch responsive

Video

High Res Version: https://www.youtube.com/watch?v=8N-5hp1FVPY

Uploaded to WordPress version

Code

/* RoboVend - A vending machine for humans and robots
* An ideate project by Andy Kong
* 
* This code detects touches on its input pins, turns on LEDs
* according to which pads are touched, and recognizes
* a preprogrammed code to turn the motor driver on. 
* 
* Shoutout to FastTouch, really made my life easier for doing touch detection
*/

#include <FastTouch.h>

int ledpins[] = {2,3,4,5,6};
int touchpins[] = {7,8,9,10,11};
const int numtouchpins = 5;
int lastTouches[numtouchpins];

int palmPin = 11; // GND the palm pin
int motPin = 13; // Pin that controls the transistor that powers the food dispenser



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

    // Set up LED pins
    for (int i = 0; i < numtouchpins; i++){
        pinMode(ledpins[i], OUTPUT);
    }
    pinMode(motPin, OUTPUT);
    pinMode(palmPin, OUTPUT);
    
}

// Create capacitive touch threshold for the fastTouchRead()
int touchMargin = 1;

void loop() {
    Serial.println("New");
    // Read from all touch pads, and trigger their LED if they're past the touch threshold
    for (int i = 0; i< numtouchpins; i++){
        lastTouches[i] = fastTouchRead(touchpins[i]);
        Serial.print("\t Pin ");
        Serial.print(i);
        Serial.print(": ");
        Serial.print(lastTouches[i]);
        
        
        if (lastTouches[i] >= touchMargin){
            digitalWrite(ledpins[i], HIGH);
            // Debuggery
//            Serial.println("pin ");
//            Serial.print(touchpins[i]);
//            Serial.println(" turning on HIGH");
        } else{
            digitalWrite(ledpins[i], LOW);
            if (i == 0)
                digitalWrite(motPin, LOW); 
        } 
    }

    // Blink if the combination of pads is correct
    if (lastTouches[0] >= touchMargin && lastTouches[1] >= touchMargin && lastTouches[2] == 0 &&
           lastTouches[3] == 0 && lastTouches[4] >= touchMargin){
        // Turning the motor for food dispenser-y
        digitalWrite(motPin, HIGH); 

        // Beeping code
        for (int i = 0; i < numtouchpins; i++){
            digitalWrite(ledpins[i], LOW);
        } 
        delay(50);
        digitalWrite(motPin, LOW); 
        delay(50);
        digitalWrite(motPin, HIGH); 
        delay(50);
        digitalWrite(motPin, LOW); 
        delay(50);
        digitalWrite(motPin, HIGH); 
        delay(50);
        digitalWrite(motPin, LOW); 
        
        // Blinking code
        for (int i = 0; i < numtouchpins; i++){
            if (i != 0){
                digitalWrite(ledpins[i-1], LOW);
            }
            digitalWrite(ledpins[i], HIGH);
            delay(100);
        }
        digitalWrite(ledpins[numtouchpins-1], LOW);
        
        for (int i = 0; i < numtouchpins; i++){
            if (i != 0){
                digitalWrite(ledpins[i-1], LOW);
            }
            digitalWrite(ledpins[i], HIGH);
            delay(100);
        }
    } else {
        digitalWrite(motPin, LOW); 
    }
    
    delay(10);
}

 

]]>
Davine Gates Documentation https://courses.ideate.cmu.edu/62-362/f2021/davine-gates-documentation/ Fri, 08 Oct 2021 18:57:01 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11122 CRT Nostalgia

Left mac mini with monitor display indicates the Millumin file pushing content to the projectors; right laptop uploads Arduino

Full color image

Cyan image

Yellow image

Magenta image

user interaction

For this project, I wanted to explore the magic analog logic of additive light mixing in a playful, nostalgic interactive experience. The eventual coordination of my three projectors simulates the effect of a single old CRT projector, and the content was similarly inspired by early childhood memories of being transfixed by cartoons playing on my grandmother’s clunky CRT TV. The experience is made more intimate and immersive because the viewport is designed for one user to interact at a time.

This simple box has a viewport on one side and a projection screen on the opposite face, and is fitted with an oblong board embedded with 4 IR proximity sensors. 3 projectors divide a video of early 2000s cartoons into their red, green, and blue channels (processed through Millumin), which combine to create a full-color image when overlaid perfectly on the screen; old CRT projectors originally had 3 lenses for each primary color, as opposed to modern digital projectors which emit the combined light through a single lens. Each projector has a light valve secured in front of the lens that becomes opaque when triggered by certain combinations of readings from the proximity sensors. When a translucent light valve receives a high electrical signal, it becomes opaque; the corresponding projector’s channel of light is removed from the combined image, resulting in the secondary colors of light: cyan, magenta, and yellow.

I had two forms of logic in this piece: the first being the science itself of mixing the primary colors of light, the second being the combinations of sensors designed to trigger the light valves.

red AND green AND blue–> white (full color)

red AND green –> yellow

red AND blue –> magenta

blue AND green –> cyan

 

Progress Images:

front of viewing box; 4 blue rectangles indicate sensor placement

rear of viewing box; red, green, and blue channel projectors will map onto a single surface

Wiring with the sensors in-board

4 sensors successfully spoke to 3 projectors through logic coded in Arduino

lightvalves

 

Process reflection:

Given that I had no prior experience with electrical hardware, I definitely struggled with initial ideation; I wasn’t sure what functional and conceptual possibilities there were given my limited knowledge, which is why I latched onto an example of simple pre-existing logic in the form of additive light mixing. If I had a stronger conceptual backing, I think I would’ve had more fun with the user experience of the piece. I ended up playing with a lot of unusual tools, namely the light valves, IR proximity sensors, and mini projectors, and I also interacted with Arduino and laser cutting for the first time on this project. Towards the end of the process, I drained a lot of time soldering and re-soldering the sensor wires in different configurations; we tried to solder the two grounds and a resistor together coming directly off of the sensor, which ended up preventing signal from reaching the breadboard. Ultimately, running into these issues actually gave me a better understanding of the electronics I was working with, and despite the setbacks, the scope of my project was surprisingly feasible.

If I had more time, I definitely would have added height to the box so the user could interact with it while seated in a chair. I also would have added more cartoon content, and if I had more time to dedicate to Arduino/software skills, it would’ve been fun if the user could “change the channel” while looking through the viewport. I also could have fine-tuned the projector positions; in the full color image you can see that there are some hotspots where a projector is off-center from the image and the distribution of color is a little uneven.

 

Final Arduino code, guided by Prof. Zacharias:

const int TOPSENSOR = A0; // shortcut to refer to the top sensor pin later
const int LEFTSENSOR = A1; // shortcut to refer to the left sensor pin later
const int RIGHTSENSOR = A2; // shortcut to refer to the right sensor pin later
const int BOTTOMSENSOR = A3; // shortcut to refer to the bottom sensor pin later

const int REDVALVE = 9;
const int GREENVALVE = 10;
const int BLUEVALVE = 11;

const int PROXTHRESHOLD = 40; // values above 40 are "high"
bool TopClose, LeftClose, RightClose, BottomClose; 

void setup() {
  pinMode(TOPSENSOR, INPUT); // we will be reading the top sensor pin as an input
  pinMode(LEFTSENSOR, INPUT); // we will be reading the left sensor pin as an input
  pinMode(RIGHTSENSOR, INPUT); // we will be reading the right sensor pin as an input
  pinMode(BOTTOMSENSOR, INPUT); // we will be reading the bottom sensor pin as an input

  pinMode(REDVALVE, OUTPUT); 
  pinMode(GREENVALVE, OUTPUT);
  pinMode(BLUEVALVE, OUTPUT);

  Serial.begin(9600); // starts serial communication at 9,600 baud (the rate)
}

void loop() {
  int readVal1; // initialize a new integer to store the photocell value
  readVal1 = analogRead(TOPSENSOR); // do the analog read and store the value

  int readVal2; // initialize a new integer to store the photocell value
  readVal2 = analogRead(LEFTSENSOR); // do the analog read and store the value

  int readVal3; // initialize a new integer to store the photocell value
  readVal3 = analogRead(RIGHTSENSOR); // do the analog read and store the value
  
  int readVal4; // initialize a new integer to store the photocell value
  readVal4 = analogRead(BOTTOMSENSOR); // do the analog read and store the value

  if (readVal1 > PROXTHRESHOLD) TopClose = true;
  else TopClose = false;
  if (readVal2 > PROXTHRESHOLD) LeftClose = true;
  else LeftClose = false;
  if (readVal3 > PROXTHRESHOLD) RightClose = true;
  else RightClose = false;
  if (readVal4 > PROXTHRESHOLD) BottomClose = true;
  else BottomClose = false;

  if ((TopClose && LeftClose) || (BottomClose && RightClose)) {
    // RedValve becomes opaque
    digitalWrite(REDVALVE, HIGH);
  } else digitalWrite(REDVALVE, LOW); // if REDVALVE is not high (opaque), return to transparent
  
  if ((TopClose && RightClose) || (BottomClose && LeftClose)) {
    // GreenValve becomes opaque
    digitalWrite(GREENVALVE, HIGH);
  } else digitalWrite(GREENVALVE, LOW); // if GREENVALVE is not high (opaque), return to transparent
  
  if (LeftClose && RightClose) {
    // BlueValve becomes opaque
    digitalWrite(BLUEVALVE, HIGH);
  } else digitalWrite(BLUEVALVE, LOW); // if BLUEVALVE is not high (opaque), return to transparent


  delay(50); // slow the loop down a bit before it repeats
}

 

]]>
Light On Schedule https://courses.ideate.cmu.edu/62-362/f2021/light-on-schedule/ Fri, 08 Oct 2021 18:47:28 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11118 Description

Today, there is an extreme focus on efficiency and scheduling an individual’s entire life. People divvy their calendars up into blocks of time when they should be working, sleeping, resting, etc. Most people don’t work that way; I certainly don’t. I can’t flip a switch and go from work mode to rest mode or vice versa. Thus, the purpose of this project is to use colored lights to influence my emotional state. By changing the colors of the lights, I’m hoping to put myself in different moods that are conductive to work, rest or any other type of activity I may choose to do.

Jayla (the digiTOOL Professor) interacting with the final project

The final project includes a clock and a series of switches. Each switch is engraved with a symbol and correlates to an activity I may engage in: work, rest, party and sleep. By flipping a switch, the colors change to a palette that triggers a helpful emotional response; however, there is a hierarchy and logic to the switches in the event that someone attempts to turn all switches on. First of all, the sleep switch must be turned to awake in order for the lights to turn on at all. Once that is done, if the work switch is flipped on, then the lights won’t change to any other color scheme even if rest or party are flipped. Rest is the next in the hierarchy, operating just like the work switch with party being the next palette in the hierarchy.

The colors chosen for each activity are as follows*:

Work: I chose red and orange colors. Reds traditionally encourage passion and hard work. Though red is typically associated with physical labor and blue with intellectual labor, I found that blue hues were too relaxing, calming and cold to put me in the mood for work. This may vary between individuals, though. Orange is commonly associated with creativity, and as someone who takes mostly project-based classes, I figured it would help to have my creativity stimulated while working.

The red and orange colored work lights

Rest: The colors I chose were many purple, pink and blue tones. All of these colors bring about a calming emotional response in me. These are dream-like colors that promote relaxation. As such, they are very fitting when I want to relax.

The purple, pink and blue colored rest lights

Party: As someone who prefers house parties over ragers, I chose yellow, pinks and blues. Yellows are energetic happy colors, pinks are intimate, gentle and fun, and blues, especially the hue I used, are very calming. These colors inspire a lot of positive emotions that fit for a party.

The yellow, pink and blue colored party lights

Awake: When the sleep switch is flipped to awake, the lights simply turn on. The color I chose for this are slightly yellow. This is because I want to be energetic and happy when I wake up, but yellow is also my favorite color so it helps me start the day with a lot of positive emotions.

The awake lights: though difficult to tell in this photo, the white lights have a yellow hue

*In video and pictures, the colors of the LEDs aren’t quite accurate to how they appear in person; the hues are ever so slightly off. Additionally, the clock flickers in video but not in person.

A picture of the clock when all lights are off and it is in sleep mode

An image for scale. The clock on my bedside table, the intended location of the final project

Process Reflection

The biggest challenge I had throughout this process was laser cutting the clock out of the frosted acrylic. The first time I laser cut the box, there were quite a few errors that made it impossible to attach the front and back. Furthermore, the measurements for the switch cut outs were slightly off so I had to also fix the cut outs. I was also unaware of how the engraving would appear, so I had to redo the images. This took up a lot of time and resources and required me to make constant edits and fixes.

The first laser cut box. The front and back are not attached as it doesn’t quite fit

The first laser cut front. The sleep/awake switch symbol appears to be just a circle and the rest symbol failed to print

On the other hand, wiring the project was fairly easy. After some reminders of circuitry that I had forgotten since the last time I had wired something, I got the lights and clock working pretty quickly. I had both working for the first prototype. In retrospect, I probably could have attempted some more advanced circuitry and coding for a more interesting result, but I am happy with how it works technically and aesthetically.

Got the lights hooked up and working

Made a box and stuffed all wires in there – including the clock I had hooked up

The first prototype. The switches don’t actually work at this point

Got the switches hooked up and working

An example of one of the more interesting directions I could have taken include making the clock an actually alarm clock. I would set the time the “alarm” would go off, which instead of making noise would just trigger the lights to change. This would have more strongly tied into the thematic idea of strict scheduling that was one of the driving ideas behind this project. Also, as mentioned in the critique, there were other ways I could have incorporated the lights to more strongly influence my emotional state. For example, it was specifically brought up that lights that are higher up encourage a person to work while lower level lights are more relaxing. These missed opportunities would have taken more work, but I believe they also would have elevated my project to another level.

Code

/* Light on Schedule
 * by Leah Walko
 * Takes input from four switches to swap the colors of LED lights
 * 7 -> work switch input
 * 6 -> rest switch input
 * 5 -> party switch input
 * 4 -> sleep/awake switch input
 */

//    HT16K33.h
//    FILE: demo_displayTime.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.2.1
// PURPOSE: demo
//     URL: http://www.adafruit.com/products/1002
//     URL: https://github.com/RobTillaart/HT16K33
//    PololuLedStrip
//  Author: Pololu
//     URL: https://github.com/pololu/pololu-led-strip-arduino
//    RTClib
//  Author: Adafruit
//     URL: https://github.com/adafruit/RTClib

// display stuff
#include "HT16K33.h"

HT16K33 seg(0x70);

/* LedStripGradient: Example Arduino sketch that shows
 * how to control an Addressable RGB LED Strip from Pololu.
 */

// LED stuff
#include <PololuLedStrip.h>

// Create an ledStrip object and specify the pin it will use.
PololuLedStrip<12> ledStrip;

// Create a buffer for holding the colors (3 bytes per color).
#define LED_COUNT 60
rgb_color colors[LED_COUNT];

// time clock
#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

// switches
int workPin = 7;
int restPin = 6;
int partyPin = 5;
int awakePin = 4;

void setup()
{

  seg.begin();
  Wire.setClock(100000);
  seg.displayOn();
  seg.setDigits(4);

  if (! rtc.begin()) {
    while (1);
  }
  
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  //switches
  pinMode(workPin, INPUT_PULLUP);
  pinMode(restPin, INPUT_PULLUP);
  pinMode(partyPin, INPUT_PULLUP);
  pinMode(awakePin, INPUT_PULLUP);
}


void loop()
{
  // switches -> pull up for true false is inverted
  boolean work = !digitalRead(workPin);
  boolean rest = !digitalRead(restPin);
  boolean party = !digitalRead(partyPin);
  boolean awake = !digitalRead(awakePin);
  
  // Set the time
  DateTime now = rtc.now();
  
  // Update the colors.
  for (uint16_t i = 0; i < LED_COUNT; i = i + 4)
  {
    if(!awake)
    {
      colors[i] = rgb_color(0, 0, 0);
      colors[i+1] = rgb_color(0, 0, 0);
      colors[i+2] = rgb_color(0, 0, 0);
      colors[i+3] = rgb_color(0, 0, 0);
    }
    if(awake && work)
    {
      colors[i] = rgb_color(1, 255, 1);
      colors[i+1] = rgb_color(50, 225, 20);
      colors[i+2] = rgb_color(87, 255, 0);
      colors[i+3] = rgb_color(50, 255, 5);
    }
    if(awake && !work && rest)
    {
      colors[i] = rgb_color(0, 255, 247);
      colors[i+1] = rgb_color(13, 194, 255);
      colors[i+2] = rgb_color(13, 129, 255);
      colors[i+3] = rgb_color(13, 93, 255);
    }
    if(awake && !(work || rest) && party)
    {
      colors[i] = rgb_color(0, 255, 202);
      colors[i+1] = rgb_color(210, 255, 0);
      colors[i+2] = rgb_color(255, 25, 246);
      colors[i+3] = rgb_color(18, 179, 146);
    }
    if(awake && !(work || rest || party))
    {
      colors[i] = rgb_color(200, 255, 30);
      colors[i+1] = rgb_color(200, 255, 30);
      colors[i+2] = rgb_color(200, 255, 30);
      colors[i+3] = rgb_color(200, 255, 30);
    }
  }


  // Write the colors to the LED strip.
  ledStrip.write(colors, LED_COUNT);

  // display time
  seg.displayTime(now.hour(), now.minute(), true, false);
  seg.displayColon(1);
}


// -- END OF FILE --
]]>
Crystal Box Fortune Teller Documentation https://courses.ideate.cmu.edu/62-362/f2021/olivia-crystal-box-fortune-teller-documentation/ Fri, 08 Oct 2021 18:10:05 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11130 By Olivia Werner

Presentation Photos:

Detail Photos:

Videos Demonstrating User Interaction and Outputs:

 

Narrative Description:

The crystal box fortune teller is a box made from mirrored acrylic with a big white button on the inside and a microphone on the side. It sits on top of a silken pedestal with a plaque of instructions. When the user holds down the button, a small white LED turns on while they ask the box a question. When the user finishes asking their question and lets go of the button, colored LEDs light up and a speaker produces a sound. There are three possible answers to whatever question the user asks so the LEDs have three different color gradients and the speaker has three different sounds.

Process Photos:

This video shows the first time I tested out the LEDs on my maquette using an example gradient arduino code.

 

Here is when I began assembling the laser cut mirrored acrylic and attaching the button component. This process was very time consuming and frustrating because I was using plastic weld to join the acrylic pieces that dried very quickly. I realized at this point that I probably should have designed better joints between the pieces because they kept breaking apart every time I handled the box.

At this point I had started coding and wiring the strip of LEDs, the button, the singular LED and I was attempting to use the DFplayer component. The DFplayer component was working at first taking in information from an SD card to play my three sounds from a speaker but eventually it started giving me troubles and I had to look for another sound producing component.

Here I had cut the LED strip into 5 pieces and soldered them all to the breadboard so I could line the inside of the box seamlessly. My next step at this point in the process was to write the code for the new sound producing component. I was using the Adafruit Music Maker arduino shield which also takes information from an SD card.

My last step was building the wooden pedestal that the crystal box would sit on.

 

Process Reflection:

This project was my first time coding arduino which was hard for me but I learned a lot of good basics in C and feel more confident with it now. Designing and constructing the box was easy for me because I have a lot of experience in model making but even then I struggled with putting the box together. I hope to continue learning C so that I can start doing more complex projects and build a better intuition for writing code. I am excited for what other electronics we will learn how to use in upcoming projects because I found this project to be really fun and satisfying when things started to work.

This project helped me learn the importance of planning on finishing a project a few days in advance because when working with electronics there will always be unexpected last minute bugs and glitches. This is something I don’t usually need to worry about in my normal architecture curriculum. I thought I was nearly finished with this project on the Friday before it was due but then that is when the DFplayer just stopped working so I had to troubleshoot and completely rethink the sound component for my project. I am grateful that I planned ahead and had the time to fix the sound issues.

If I had changed the input to be something other than a button then my project could have taken an entirely new direction. My outputs were based on how long the button was being held down so if I had used a different kind of sensor instead, the logic of my code would have been different. I also could have changed the size, shape and siting of the box to create a completely different user experience. Some suggestions that were brought up during my review were to scale up the size of the box so that people could fit their head inside of the infinity mirror. I also would consider putting mirrors on the outside of the box and having a default light gradient to make the project more visually interesting when the button is not being pressed.

Code submission:

/* Crystal Box Fortune Teller
    by Olivia Werner
    The user input for this code is a button. Other components involved
    are the pololu LED strip, a singular LED, and the Adafruit Music Maker.
    It records how long the button has been pressed, sorts the input into
    three different color modes and uses nested for loops to output a different
    LED gradient and sound for each color mode.
*/

// pin 2 for button
//pin A2 led strips
//pin A3 resistor to singular led


/*button
  created 2005
  by DojoDave < http: //www.0j0.org>
  modified 30 Aug 2011
  by Tom Igoe*/
#include <PololuLedStrip.h>
#include "Arduino.h"
#include "SoftwareSerial.h"
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>

//speaker
// These are the pins used for the breakout example
#define BREAKOUT_RESET  9      // VS1053 reset pin (output)
#define BREAKOUT_CS     10     // VS1053 chip select pin (output)
#define BREAKOUT_DCS    8      // VS1053 Data/command select pin (output)
// These are the pins used for the music maker shield
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)

// These are common pins between breakout and shield
#define CARDCS 4     // Card chip select pin
// DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin

Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);

// Create an ledStrip object and specify the pin it will use.
PololuLedStrip<A2> ledStrip;

// Create a buffer for holding the colors (3 bytes per color).
#define LED_COUNT 60
rgb_color colors[LED_COUNT];


// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  A3;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status
int prevButtonState = 0;

bool countingUp = true;

unsigned long startTime = 0;
unsigned long Dif = 0;

int RoundDif = 0;
int ColorMode = 3;

void setup() {
  // put your setup code here, to run once:
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);

  if (! musicPlayer.begin()) { // initialise the music player
    Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
    while (1);
  }
  Serial.println(F("VS1053 found"));

  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }

  // list files
  printDirectory(SD.open("/"), 0);

  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(1, 1);

  // If DREQ is on an interrupt pin (on uno, #2 or #3) we can do background
  // audio playing
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int

}

void loop() {
  // put your main code here, to run repeatedly:
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH && prevButtonState == LOW) {
    //begin timer
    digitalWrite(ledPin, HIGH);
    startTime = millis();
    delay(10);
  }
  if (buttonState == LOW && prevButtonState == HIGH) {
    //end timer
    digitalWrite(ledPin, LOW);
    delay(10);
    Dif = millis() - startTime;
    RoundDif = Dif / 1000;

    if (RoundDif > 3) {
      ColorMode = 0;
      Serial.println ("Concentrate and Try Again");
      // play file 001
      delay (1500);
    } else {
      if (RoundDif % 2 == 0) {
        ColorMode = 2;
        Serial.println ("Yes, even");
        // play file 003
        delay (1500);
      } else {
        ColorMode = 1;
        Serial.println ("No, odd");
        // play file 002
        delay (1500);
      }
    }
  }

  /*
    // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
    if (buttonState == HIGH) {
      // turn LED on:
      digitalWrite(ledPin, HIGH);
    } else {
      // turn LED off:

    }*/

  // Write the colors to the LED strip.

  if (ColorMode == 2)  {
    gradient(80, 100, "/track003.mp3"); //Play gradient and file 003 mp3
    ColorMode = 3;
  }
  else if (ColorMode == 1) {
    gradient(230, 255, "/track002.mp3"); //Play gradient and file 002 mp3
    ColorMode = 3;
  }
  else if (ColorMode == 0) {
    gradient(10, 30, "/track001.mp3"); //Play gradient and file 001 mp3
    ColorMode = 3;
  }
  if (ColorMode == 3) {
    for (uint16_t i = 0; i < LED_COUNT; i++) {
      colors[i] = rgb_color(0, 0, 0);
    }
    ledStrip.write(colors, LED_COUNT);
  }

  prevButtonState = buttonState;
}

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

//gradient function

void gradient(int beginhue, int endhue, int mp3) {
  for (int y = 0; y < 3; y++) {
    for (int hue = beginhue; hue < endhue; hue++) {
      for (uint16_t i = 0; i < LED_COUNT; i++) {
        colors[i] = Wheel(hue);
      }
      ledStrip.write(colors, LED_COUNT);
      delay(70);
    }

    if (y == 0) musicPlayer.playFullFile(mp3); //Play mp3
    for (int hue = endhue; hue > beginhue; hue--) {
      for (uint16_t i = 0; i < LED_COUNT; i++) {
        colors[i] = Wheel(hue);
      }
      ledStrip.write(colors, LED_COUNT);
      delay(70);
    }
  }
}

/// File listing helper
void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      //Serial.println("**nomorefiles**");
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

 

]]>
Bus Tracker https://courses.ideate.cmu.edu/62-362/f2021/bus-tracker/ Fri, 08 Oct 2021 17:48:53 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11192 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> How well can you binary? https://courses.ideate.cmu.edu/62-362/f2021/how-well-can-you-binary/ Thu, 07 Oct 2021 22:40:36 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11194 Project Title: How well can you binary?

 

Basic setup + loading screen

Normal test_smol

Video showing a run through of the piece

GOOD result_smol

Video showing a run through of the piece fast enough to be judged as “good”

AND fail_smol

Video showing what happens if you try to press both buttons

Leah taking the “How well can you binary?” test

User experience

Super secret wiring contained in the podium: Arduino connected to buttons and the “AND” logic gate breadboard setup.

 

Simple narrative description:

There is a podium with two buttons on it in front of a projection. When the user presses a button the “test” begins, asking the user to choose between two different options. If the user tries to press both buttons at once a loud buzzer goes off and the projection flashes “AND is not an option.” Each time the user answers a question a ticking noise gets faster. After the last question the test/projection tells the user if they were “BAD” or “GOOD” based on how fast they were able to answer the questions.

Process Images:

Tiny test of the two buttons to arduino setup

Layout of various tasks mid-process

Early code testing the two button input before adding to main body of code

Figuring out how to add sound

Adding sound to the basic question setup

Testing the code before the buttons arrived using mouse+ keyboard inputs

Process Reflection: 

Surprisingly for me the easier aspect of the project was the technical parts. In the past I have struggled the most with the coding and the wiring of the piece but I think I did an okay job giving myself a project that was relatively approachable for someone with my level of experience. It was difficult enough coding-wise where I definitely learned something (I’ve never used Processing before) but not so difficult that I saw it as a major challenge.

I think the biggest hurdle that I had to tackle was conceptual: how do I make sure my piece is meaningful? Or maybe: how do I make sure what I imagined as being “good” about the project comes through? This, for me, was more of a design problem than anything else.  I wanted to make sure the piece had an element of absurdity while still making the message meaningful, and I didn’t know if there was any way to test whether or not I had crossed that line until I actually presented the thing. I think something akin to the 90% critique we are going to have for the next project would have helped me resolve this problem/these doubts before the final presentation. I think what was interesting to find out during the final critique was how people wanted to explore the piece more in terms of figuring out the different outcomes (ie rushing through the questions to get to the “you are…GOOD” outcome) as opposed to focusing on the meaning behind the questions themselves. I wonder if there is some element of the “game” that could have been changed to focus more thoroughly on the conceptual meaning behind the piece.

Elements such as messing with the question format and making the podium a binary in and of itself, suggestions I received during the critique from our guest critics, definitely might have improved this aspect of the “game.” In the end I learned that the design of the thing (the appearance of the interface, how it moved between questions, what the podium looked like) for me at least, ended up being the biggest hurdle, and something I needed more feedback on, than the technical problems I expected to struggle with more. In the future I need to focus more on getting the technical stuff out of the way earlier so that I can have people interact with the piece earlier on in order to incorporate design feedback to the functionality of the piece before the final critique.

Arduino Code:

/*
How well can you binary?
Kirman Hanson

- The code reads the input coming from the buttons and through the "AND" gate circuit (set to the 8,7, and 4 pins on the arduino)
and sends the information to the processing code as either a "0", "2", "3", or "4" depending on the input.

- code is based on the basic "digital button" sketch provided with the arduino

*/

// constants won't change. They're used here to set pin numbers:
const int leftButtonPin = 8;     // the number of the left pushbutton pin
const int rightButtonPin = 7;  // the number of the right pushbutton pin
const int andPin = 4;

// variables will change:
int leftButtonState = 0;         // variable for reading the left pushbutton status
int rightButtonState = 0;       // variable for reading the right pushbutton status
int andState = 0;


void setup() {
  // initialize the pushbuttons + AND gate pin as inputs:
  pinMode(leftButtonPin, INPUT);
  pinMode(rightButtonPin, INPUT);
  pinMode (andPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  // read the state of the pushbutton value:
  leftButtonState = digitalRead(leftButtonPin);
  rightButtonState = digitalRead(rightButtonPin);
  andState = digitalRead(andPin);

  if (andState == HIGH) {           // if the and gate is activated...
    Serial.write(3);                //send a 3 to Processing
    Serial.write(0);
    //delay (500);
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  }
  else if (leftButtonState == HIGH) { //send a 4 to processing if the left button is pressed
    delay(100);                       // delays help prevent the code from sending out "double presses"
    Serial.write(4);
    Serial.write(0);
    //Serial.print("left");           // these are commented out but used them to test the button input
    //Serial.print("off");
    delay(500);   
  } 
  else if (rightButtonState == HIGH) { // send a 2 to processing if the right button is pressed
    delay(100);
    Serial.write(2);
    Serial.write(0);
    //Serial.print("right");
   // Serial.print("off");
   delay (500);
    }
  else {
    Serial.write(0);                  // if nothing is happening send 0 to Processing
  }
   delay(50);                            // Wait 50 milliseconds

Processing Code:

/*
How well can you binary?
Kirman Hanson
Code draws up the interface for the game/test of the piece and 
it changes the interface in response to the button presses as inputs being
read by the arduino code.

Basic structure for code is based off the basic "serial read" example
that comes with Processing

Zach + classmates helped with troubleshooting, Erin with the design elements
*/
import processing.serial.*;
import processing.sound.*;
Serial myPort;  // Create object from Serial class
int buttonVal;      // Data received from the serial port
PFont f, qFont, andFont, startFont;
StringList qInventory, firstAInventory, secondAInventory;
int qCount = 0;
boolean andTrigger = false;  // this bool is used to trigger the "AND" state (ie the buzzer)
boolean leftHighlight = false;
boolean rightHighlight = false;
boolean buttonPressed = false;
boolean flickerLoad = false;
int warningStart = 100000;
SoundFile ticker, endBeep, endBeep2, wrongNoise;
float tickerRate = 2.0;
String tickerFile = "tick2.0.wav";
float amp = 0;
float questionStart;
float timeToAnswer;
float totalTime;
boolean endStart = false;
float endTime;

void setup() {
  // draws the basic interface
  size(1400,900);
  f = createFont("KohinoorDevanagari-Bold", 40);
  qFont = createFont("KohinoorDevanagari-Regular", 54);
  andFont = createFont("KohinoorDevanagari-Bold", 90);
  startFont = createFont("KohinoorDevanagari-Regular", 30);
  // all "inventories" are string lists containing the questions and/or answers. Change as you wish.
  qInventory = new StringList("The lights are...", "It is...", "I am...", "I am...","The sky is...", "I am a student", "I am in a ___ mood", "I am...", "I am only good at...", "I am a...", "I ___ therefore I am", "I am good at making decisions", "I am...", "Carngie Mellon is...", "I am a...", "I am...", "I am...", "I am...", "I am...", "I am...");
  firstAInventory = new StringList("on", "cold", "inside", "left-handed", "up", "yes", "good", "being", "art", "good person","think", "yes", "strong", "ethical", "success", "lucky", "not fun", "spiritual", "natural", "on");
  println(firstAInventory.size()); // helps to diagnose whether you have enough answers for your questions
  secondAInventory = new StringList("off", "hot", "outside", "right-handed", "down", "no", "bad", "becoming", "science", "bad person", "feel", "no", "weak", "unethical", "failure", "hard-working", "fun", "rational", "unnatural", "off");
  ticker = new SoundFile(this, tickerFile); 
  ticker.loop(1,amp); // initializes the "ticking" noise
  println(qInventory.size()); 
  println(secondAInventory.size());
  // button input
  String portName = Serial.list()[4];
  myPort = new Serial(this, portName, 9600);
}
void draw() {
  background(255);
  fill(0);
  textAlign(CENTER);
  
  if ( myPort.available() > 0) {  // If data is available,
    buttonVal = myPort.read();         // read it and store it in val
  }
 
 if (buttonPressed == false){ // tests whether or not the button has been pressed
     background(255);
     amp = 0; // if not ticker is silent
  if (buttonVal == 4 || buttonVal == 2){ //gets the interface out of "start" mode
    buttonPressed = true;
    println("button pressed now true");
    amp = 0.05; // ticker is now able to be heard
    }
  else if (second()%2 == 0){ // sets up the "start mode", uses modulus to "blink" the text
    fill(0);
    textFont(startFont);
    text("How well can you binary?", width/2, height*0.333);
    // every 2 seconds flip the bool in order to create "flicker" effect
    if (flickerLoad == false){ 
      //println("flipped to true");
      delay(10);
      flickerLoad = true;
      delay(10);
      }
    else if(flickerLoad==true){
    flickerLoad = false;
    }
   }
    else if(flickerLoad == true){
    fill(0);
    textFont(qFont);
    text("PRESS ANY BUTTON TO START", width/2, height/2);
  }
} 

  else if (buttonVal == 3) { // if the arduino sends out a 3,triggers the "AND" state
    println("and triggered");
    fill(255);
    warningStart = millis();
    if (millis() - warningStart < 1200){ //only displays for 1.2 seconds
      andTrigger = true;
    //play dramatic tone here
      }
    else {
      andTrigger = false; // automatically ends after 1.2 seconds
    }
  }
    
else if ((buttonVal == 4 || buttonVal== 2)&& buttonVal!=3 && endStart == false) {
    //moves onto next question + triggers end state if we are at the end of the list of questions
    delay(100);
    qCount +=1; // the q count counts the number of questions answered
    andTrigger = false;
    println("tickerRate", tickerRate);
    if (qCount < qInventory.size()) {
      tickerRateIncrease(buttonVal); //increases the speed of the ticker everytime question is answered
      }
    else { // triggers the "end game" function once at end of the questions
       endTime = millis();
       ticker.stop();
       endBeep = new SoundFile(this, "endbeep.mp3");
       endBeep2 = new SoundFile(this, "endbeep_extra.wav");
       endBeep.playFor(2.0);
       endStart = true;
       println("endStart set to true");
      }
  }
 
  else if (qCount < qInventory.size()) { //displays the different questions so long as there are questions left
    background(255);
    textFont(qFont);
    text(qInventory.get(qCount), width* 0.5, height*0.333);
    textFont(f);
    //set up highlights when button pressed
    if (leftHighlight==true && millis()- questionStart < 300 && andTrigger==false){
      println("left Highlight", leftHighlight);
      rectMode(CENTER);
      fill(0);
      rect(width*0.333, height*0.666, 200, 80);
      fill(255);
      text(firstAInventory.get(qCount), width*0.333, height*0.666);//retrieves the correstpodning answer from the list
      println(qCount);
      }
    else if (rightHighlight==true && millis()- questionStart < 300 && andTrigger==false){
      println("right highlight", rightHighlight);
      rectMode(CENTER);
      fill(0);
      rect(width*0.666, height*0.666, 200, 80);
      fill(255);
      text(secondAInventory.get(qCount), width*0.666, height*0.666);//retrieves the correstpodning answer from the list
      println(qCount);
      } 
     else{
       // "normal" question display
       fill(0);
       text(secondAInventory.get(qCount), width*0.666, height*0.666);
       text(firstAInventory.get(qCount), width*0.333, height*0.666);
       //if neither is picked don't highlight it
       rightHighlight = false; 
       leftHighlight = false;
     }
    }
  // this function sets up the "AND" state
  if (millis() - warningStart < 1200 && andTrigger==true){
        wrongNoise = new SoundFile(this, "wrong.wav");
        wrongNoise.play();
        delay(90);
        fill(255,0,0);
        background(255);
        textFont(andFont);
        text("AND is not an option", width/2, height/2);
      }  
  // this sequence is the end state
  if (endStart == true){
    println("end state");
    ticker.stop();

    if (endStart==true && millis()-endTime < 2500){
      println("end game displayed");
      textFont(andFont);
      text("END GAME", width/2, height/2);
      println("endTime", endTime);
      println("endStart", endStart);
      println("millis-endTime", millis()-endTime);
      println("millis", millis());
    }
    if (millis()-endTime < 8000 && millis()-endTime >= 2500){
      println("calculating score displayed");
      textFont(qFont);
      text("Calculating results...", width/2, height/2);
    }
    if (endStart==true && millis()-endTime < 10000 && millis()-endTime >= 8000){
      println("you are...");
      textFont(qFont);
      text("You are...", width/2, height/2);
    }
    if (endStart==true && millis()-endTime < 12000 && millis()-endTime >= 10000 && totalTime <= 30000){
      textFont(andFont);
      background(0,255,0);
      fill(255);
      text("GOOD", width/2, height/2);
    }
    if (endStart==true && millis()-endTime < 12000 && millis()-endTime >= 10000 && totalTime > 30000){
      textFont(andFont);
      background(255,0,0);
      text("BAD", width/2, height/2);
    }
    if(endStart == true && millis()-endTime >= 14000){
      println("went to no loop");
      println(endTime);
      text("", width/2, height/2);
      fill(0);
      noLoop();
    }
  }
 }
void tickerRateIncrease(int buttonVal) { //this function controls the ticker rate
      println("click");
     if (buttonVal == 4){
       leftHighlight = true;
     }
     if (buttonVal== 2){
       rightHighlight = true;
     }
     //increase rate + loudness
     tickerRate -= 0.1;
     amp += 0.05;
     // endstate calculations
     timeToAnswer = millis() - questionStart;
     totalTime += timeToAnswer;
     questionStart = millis();
     //changing rates
     String rateString = nf(tickerRate, 1, 1);
     println("new ticker rate", tickerRate);
     String[] tickerParts = new String[3]; 
        tickerParts[0] = "tick"; 
        tickerParts[1] = rateString; 
        tickerParts[2] = ".wav"; 
     String tickerFile = join(tickerParts, ""); //sets up new ticker file name to be retrieved
    ticker.stop();
    ticker = new SoundFile(this, tickerFile);
    ticker.cue(0.3);
    ticker.loop(1,amp);//plays new, faster, ticker file
}

 

]]>
Post-In-Process Critique: Andy https://courses.ideate.cmu.edu/62-362/f2021/post-in-process-critique-andy/ Fri, 24 Sep 2021 06:17:46 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11090 Robovend 1: Maquette and further thoughts on robots

Still not sure exactly where to situate this Robovend machine.

In one case, I envision the Robovend as a form of interactive art in the future, in some museum. It was the first collaborative vending machine that both humans and sentient human-robots (hubots) could use, and the display plaque would make that clear. There would be photos of robots and humans posing and using it in the factory where it was first  installed, and some vaguely doom -y phrasing like “remained active until lack of humans needing to use it, whereupon it was replaced with a robot-only vending machine.” Perfect 50-50 split of functionality, robo-USBs on one side and human buttons on the other.

<- Partner with these guys

 

In another, the robovend could be more robot focused. Maybe the robots took over, or just evolved better than we did, and because of that humans have a smaller place in the planet or market than we have now. The vending machine would still have elements of the human side, but definitely a secondhand thought. Human use would be cumbersome, and most instructions would be in robocode.

Maquette: 

  • There will be many different kinds of robots, shapes and sizes and modes of getting around, so the machine should feature many forms of payment at different heights. Hence, the USB slots at different heights
  • The first robotic vending machine will be made by humans, because it requires an ability to create something that does not exist yet and I do not think the robots will be creative enough.
  • OR, it should be inconvenient for humans as an afterthought, as a form of negligence. But not outright.

Interesting directions: the parallel between us and robots (which are internet connected), and the parallel between older non-technology users and the younger, tech generation.

Also interesting: dispense robot parts and human food on reels, like pick-n-place machines.

I thought a lot about robots of the future, and how exactly they would integrate with humans in the same space. I came to a few conclusions that helped guide my intent and artifact, and then my friendly classmates and teachers gave me some more suggestions.

]]>
In Progress CRITIQUE: Tushhar Saha https://courses.ideate.cmu.edu/62-362/f2021/in-progress-critique-tushhar-saha/ Wed, 22 Sep 2021 03:10:59 +0000 https://courses.ideate.cmu.edu/62-362/f2021/?p=11023 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]>