tanvib@andrew.cmu.edu – Intro to Physical Computing: Student Work spring 2019 https://courses.ideate.cmu.edu/60-223/s2019/work Intro to Physical Computing: Student Work Wed, 15 Sep 2021 20:28:12 +0000 en-US hourly 1 https://wordpress.org/?v=5.0.21 Strength Balance by Team Gary: Final Documentation https://courses.ideate.cmu.edu/60-223/s2019/work/strength-balance-by-team-gary-final-documentation/ Fri, 10 May 2019 17:15:39 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=7603 With this project, we set out to design and build a device to help an older person in the Pittsburgh community. After an initial meeting with Gary (the older person who we partnered with), we decided to create a way to help people who are recovering from a stroke to quantitatively measure the resulting strength differences between both sides of their body. We spent several weeks creating a prototype, and received lots of valuable feedback that ultimately led us to this final product.

 

What We Built

We created a device that enables users to perform several different physical exercises such as bicep curls, lateral raises, and many other resistance band exercises in a home gym setting. They are then able to access information on their performance including max weight, the number of repetitions, and how their left and right sides compare to each other. To accomplish this, we decided on anchoring a sensor connected to a resistance band to the base of a door since we saw this as a convenient location that everyone would have in their homes. Adjacent to the sensor is an interface that users can use to see the results of their exercises.

The device has a main display that welcomes the user and then prompts them to begin an exercise on either their left or right side. Once a side is selected, there is a brief countdown to allow the user to get into position and begin the exercise. After the exercise is completed, the user can either perform another exercise on the other side or view their results at any time.

Device Functionality
Project Photos

There is an adjustable metal bracket that allows the device to be anchored underneath doors of varying widths. Foam/rubber pads protect the door and prevent the device from sliding horizontally along the bottom of the door.

Connected directly to the metal bracket is a load cell that measures the force pulling on it from a resistance band. The load cell is connected in such a way that it can pivot in a 90 degree arc allowing for many more exercises to be done.

On the bottom side of the device is a removable panel that gives users access to the battery holder in order to change batteries once they are worn out. Additionally, to save battery, there is an on/off switch on the side of the device so that the Arduino only draws power when the device is in use.

The device is anchored by ensuring that the adjustable bracket is set to the correct width and then sliding it underneath a door.

A user performing a bicep curl.

Users can change the resistance by stepping further/closer to the door and then performing their exercise.

By pressing buttons on the device, users can change sides, switch exercises, and view the results of their workout.

The following is a brief narrative sketch to help describe how our device fits in the context of everyday life:

Gary suffered a stroke and has been working hard in his home gym to correct the resulting muscle imbalance. While Gary has a trainer to help him do this, neither Gary nor his trainer have a way to quantitatively measure fine differences in strength on either side of Gary’s body.

At the beginning of each week, Gary performs several exercises such as bicep curls, lateral raises, and leg lifts using the device, and notes which side of his body is stronger and by how much. He is then able to work with his trainer to structure his week’s workout routine with the goal of balancing his strength accordingly. He is able to use the device at any point (similar to how one might use a scale to check their weight) in order to see his progress and have hard numbers to work with in improving his physical strength and becoming more balanced.

 

How We Got Here

The load cell sensor we were worked with

One of the first decisions we made as a team was the choice of sensors. Due to the form factor and flexibility we needed in our solution, a load cell seemed like the best bet, but we also initially considered using pressure sensors. The choice of load cell was the decision we look back on and wish we could have changed. We selected a 40kg bearing load cell from Amazon, which also came with an integrated board that amplified signals from the sensor and worked perfectly with an Arduino. Not only was the hardware supposed to integrate perfectly with our Arduino, but the product included a well written tutorial on how to capture signals from the load cell. In being content with the tight integration with our Arduino, however, we failed to consider just how flimsy and difficult the load cell would be to work with.

Resoldering the broken wires on the load cell

The load cell was designed to support 40kg, but the wires that detected changes in force were extremely thin and connected to the sensor with a fine plastic coating. This coating barely held the wires in place and we ended up tearing one of the wires within a few days of development. Attempts to solder it back were fruitless because there was limited wire to metal contact and the plastic coating had burned. In the end, a new, second sensor that we took extreme precaution with also broke, leading us to conclude that a more durable load cell would have been better from the get go. In fact, we have ordered a new one to be used in the final product we give to Gary.

Full electronic prototype, with working load cell, buttons, and screen interface

The lesson we learned from this was to first analyze how a physical component would perform in the real world as opposed to determining its simplicity for use with hardware or software. The sensor not only created trouble when assembling and testing our device, but also in the software development phase because we didn’t know if the problem was with the physical load cell or with the software. Before we knew that it was the weak wiring that was causing the problem, this was highly frustrating.

One of the challenging aspects of our project was repetition counting and weight measurement. After discussing with our professor and understanding the processing limits of the Arduino, we decided to implement a windowing approach. Our code kept a few hundred milliseconds of data and averages in buffers and calculated local maxima based on the slope of the data.

Signals captured from exercise repetitions, when the sensor was working

When we tested this on actual measurements from the device, it worked extremely well. This was a breakthrough moment for us and helped refocus our efforts on the physical and reliability aspects of the device. In general, the software aspect of our project was not very time consuming and we were done with that part ahead of schedule.

Testing the load cell while it was not working, not knowing what the problem was (feat. helpful non-class member)

Frustration with device – it was not communicating with the computer

The physical construction of the device was something we discussed at length. At first, we were debating whether to use a device mounted to the door (as in our final product) or to use something in line with a resistance band, to measure the force exerted by pulling on both sides of the band. Then, we needed to decide how best to house the load cell sensor as well as the screen and buttons interface – we considered having two parts to the project, so that the screen and buttons could perhaps be a separate handheld component. However, we decided against this as it would require two Arduinos and thus two sources of power, making the whole setup less clean. We eventually settled on a door-mounted, single-box design.

Sketches of ideas for housing the device and load cell

Assembling the housing using plastic solvent

Fitting the electronics into the housing. Finding an intuitive way to organize the Arduino, sensors, battery holder, etc. proved more challenging than we initially thought.

We didn’t follow our schedule too well and underestimated how long it would take to physically wire up and build the device, but were able to manage the rest of our time fairly effectively. This was possible because we delegated certain aspects of the project to each group member according to their strengths.

 

Conclusions and Lessons Learned

Several people stated that they “would like to see a smaller device” more in line with the resistance band, as something smaller might be more convenient to use on a regular basis. We had already recognized this as a way to continue improving our design, and were relatively unsurprised that the size of the device was brought up by multiple people. However, a comment stated that it was unfortunate that the user had to bend down to press the buttons, which is a good point. We thought about ways we could have fixed this, but couldn’t find any solutions that didn’t involve having another, separate part of the device,

Many of our reviews mention that the device “feels fragile and makes the user afraid to apply full force”, although the force is not applied directly to the box. During the final crit, we found ourselves explaining this to people repeatedly, which is a sign that we should have made our device visibly appear more sturdy. We believe this perception of our device could be due to the transparency of the acrylic used for the housing, although this has no impact on its actual strength. We did not realize how much this would affect people’s perception of the device.

Our device intrigued the Osher students who observed it, and throughout the crit we were asked if the device could be applied to multiple different exercises. We showed them different ways to hold the resistance band to work different muscles, but a prevailing idea seemed to be to provide multiple attachments for the device (not just the resistance band). Our device is already capable of doing this, since the attachment would just need to be able to connect to a carabiner, but this feedback reinforced the idea of preserving the adaptability of our device to multiple exercises and situations. For example, having our device be able to handle “different door placements”, different exercises, and different amounts of force.

Working with Gary was a pleasure – he had a clear idea of what he wanted from the very beginning, which surprised us but was very helpful in getting us started. He was always available for feedback, and seemed very happy with the direction we were going in. However, we were working somewhat blindly, as he didn’t tell us what specific exercises he did and we just worked off our best guess of what he needed. We happened to make the right choices, but were I to do this again I would make sure to ask for all the information we need from the start.

Overall, we had a relatively even distribution of work over time, but fell off schedule and spent a large portion of a day putting everything together. Doing this again, we would attempt to stay on track and finish different parts of the project by the scheduled times. This way, we would be able to achieve our longer-term goals, such as making our design more compact by using a smaller Arduino or microcontroller.

It was a very rewarding experience to be able to make something suited to someone we have been talking to for over a month, and have it suit his specific needs, but also be applicable to many other situations. Gary actually wants to take this project forward and develop it into a more marketable product, which is unexpected, but it is very exciting that Gary thinks that our product has potential in the market.

Technical Details

Code

/*
 * Strength Balance
 * 
 * Description: We created a device that enables users to perform several different physical exercises 
 * such as bicep curls, lateral raises, and many other resistance band exercises in a home gym setting. 
 * They are then able to access information on their performance including max weight, the number of 
 * repetitions, and how their left and right sides compare to each other. To accomplish this, 
 * we decided on anchoring a sensor connected to a resistance band to the base of a door since we saw 
 * this as a convenient location that everyone would have in their homes. Adjacent to the sensor is an 
 * interface that users can use to see the results of their exercises.
 * 
 * 
 * This code takes load cell data, processes it, and displays the results on a  
 * screen. It also responds to user input, by means of thee buttons.
 * Input: 3 buttons, 1 load cell sensor || Output: 1 LCD screen
 * 
 * PIN 2  = Load Cell Sensor Pin CLK
 * PIN 3  = Load Cell Sensor Pin DOUT
 * PIN 8  = Clear Button
 * PIN 9  = Right Button
 * PIN 10 = Left Button
 * 
 */
#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Queue.h"
#include "HX711.h"

#define DOUT  3 // Load Cell pin 1
#define CLK  2 // Load Cell pin 2
#define BUTX 8 // Clear Button
#define BUTR 9
#define BUTL 10
#define CALIBRATION_FACTOR -7050
#define WINDOW_SIZE 5
#define AVERAGES_SIZE 5

LiquidCrystal_I2C screen(0x27, 20, 4);

HX711 scale;

const char LEFT = 0;
const char RIGHT = 1;

float calibrationFactor = -7050; 
//calibration factor used by HX711 library to do some sort of data filtering
unsigned long startMillis;
unsigned long timerMillis;
unsigned long currentMillis;
int exerciseTimer = 0;
int rightPressed = 1;
int leftPressed = 1;
int clearPressed = 1;
bool exerciseMode = false;
int leftMax = 0;
int rightMax = 0;
bool leftActivated = false;
bool rightActivated = false;
bool leftInUse = false;
bool rightInUse = false;

// window and average queues
Queue<int> window = Queue<int>(WINDOW_SIZE);
int windowSum = 0;
Queue<float> averages = Queue<float>(AVERAGES_SIZE);
float averagesSum = 0;
// variables to help with repetition counting
String oldSlope = "flat";
int repLeftCount = 0;
int repLeftWeightSum = 0;
int repRightCount = 0;
int repRightWeightSum = 0;

//---  HELPERS  ---

void initLoadCell()
{
  scale.begin(DOUT, CLK);
  scale.set_scale(CALIBRATION_FACTOR);
  scale.tare(); //Reset the scale to 0
}

void homeScreen() {
  screen.home();
  screen.clear();
  screen.print("Press L/R to begin");
  screen.setCursor(0, 1);
  screen.print("and end exercises.");
  screen.setCursor(0, 2);
  screen.print("Press X for results.");
  screen.display();
}

void beginExDisp() {
  for (int i = 5; i > 0; i--) {
    screen.home();
    screen.clear();
    screen.print("Start in ");
    screen.print(i);
    screen.print(" seconds");
    screen.display();
    delay(1000);
  }
  screen.clear();
  screen.print("Go!");
  delay(500);
  screen.display();
  startMillis = millis();
  exerciseTimer = 0;
}

void handleButtonSide(char side) {
  beginExDisp();
  if (side == LEFT)
  {
    leftActivated = true;
    leftInUse = true;
    rightInUse = false;
  }
  else
  {
    rightActivated = true;
    leftInUse = false;
    rightInUse = true;
  }
}

void handleButtonClear() {
  // if both the left and right buttons were pressed during 
  // the last exercise set
  screen.clear();
  if (leftActivated && rightActivated) 
  {
    screen.print("Left Max: ");
    screen.print(leftMax);
    screen.setCursor(0, 1);
    screen.print("Reps: ");
    screen.print(repLeftCount);
    screen.setCursor(0, 2);
    screen.print("Avg Weight: ");
    float leftAvg = (float)repLeftWeightSum / (float)repLeftCount;
    screen.print(leftAvg);
    delay(3000);
    screen.clear();
    screen.print("Right Max: ");
    screen.print(rightMax);
    screen.setCursor(0, 1);
    screen.print("Reps: ");
    screen.print(repRightCount);
    screen.setCursor(0, 2);
    screen.print("Avg Weight: ");
    float rightAvg = (float)repRightWeightSum / (float)repRightCount;
    screen.print(rightAvg);
    delay(3000);
    if (!(leftAvg == 0)) {
      screen.clear();
      screen.print("Right : Left = ");
      screen.setCursor(0, 1);
      screen.print(rightAvg);
      screen.print(" : ");
      screen.print(leftAvg);
      delay(3000);
    }
  }
  else if (leftActivated)
  {
    screen.print("Left Max: ");
    screen.print(leftMax);
    screen.setCursor(0, 1);
    screen.print("Reps: ");
    screen.print(repLeftCount);
    screen.setCursor(0, 2);
    screen.print("Avg Weight: ");
    float leftAvg = (float)repLeftWeightSum / (float)repLeftCount;
    Serial.print(leftAvg);
    screen.print(leftAvg);
    delay(3000);
  }
  else if (rightActivated) 
  {
    screen.print("Right Max: ");
    screen.print(rightMax);
    screen.setCursor(0, 1);
    screen.print("Reps: ");
    screen.print(repRightCount);
    screen.setCursor(0, 2);
    screen.print("Avg Weight: ");
    float rightAvg = (float)repRightWeightSum / (float)repRightCount;
    Serial.print(rightAvg);
    screen.print(rightAvg);
    delay(3000);
  }
  else
  {
    screen.print("No reps counted!");
    delay(500);
  }

  // reset globals
  leftMax = 0.0;
  rightMax = 0.0;
  leftActivated = false;
  rightActivated = false;
  leftInUse = false;
  rightInUse = false;
  homeScreen();
  initLoadCell();
  repLeftCount = 0;
  repLeftWeightSum = 0;
  repRightCount = 0;
  repRightWeightSum = 0;
  Serial.println("reset reps");
  
}

//---  MAIN CODE  ---

void setup() {
  Serial.begin(9600);
  pinMode(BUTR, INPUT_PULLUP);
  pinMode(BUTL, INPUT_PULLUP);
  pinMode(BUTX, INPUT_PULLUP);
  
  screen.init();
  screen.backlight();
  screen.print("Welcome, Gary!");
  delay(1000);
  homeScreen();

  scale.begin(DOUT, CLK);
  scale.set_scale(CALIBRATION_FACTOR);
  scale.tare();
  
  startMillis = millis();
  timerMillis = millis();
}


void loop() {
  screen.home();
  scale.set_scale(calibrationFactor);
  
  currentMillis = millis();

  rightPressed = !digitalRead(BUTR);
  leftPressed = !digitalRead(BUTL);
  clearPressed = !digitalRead(BUTX);

  if (clearPressed)
  {
    exerciseMode = false;
    handleButtonClear();
  }
  else if (leftPressed)
  {
    exerciseMode = true;
    handleButtonSide(LEFT);
  }
  else if (rightPressed)
  {
    exerciseMode = true;
    handleButtonSide(RIGHT);
  }
  
  // figure out average from window
  int newReading = abs(scale.get_units());
  window.push(newReading);

  float newAverage;
  if(window.count() >= WINDOW_SIZE)
  {
    int oldReading = window.pop();
    windowSum += newReading;
    windowSum -= oldReading;
    newAverage = float(windowSum) / WINDOW_SIZE;
  }
  else
  {
    windowSum += newReading;
    newAverage = float(windowSum) / window.count();
  }

  // figure out up/down from average
  averages.push(newAverage);

  float oldTotal = averagesSum;
  float newTotal;
  
  if(averages.count() >= AVERAGES_SIZE)
  {
    float oldAverage = averages.pop();
    averagesSum += newAverage;
    averagesSum -= oldAverage;
    newTotal = averagesSum;
  }
  else
  {
    averagesSum += newAverage;
    newTotal = averagesSum;
  }

  // decide if up/down, adjust vars
  // figure out the new slope
  float difference = newTotal - oldTotal;
  String newSlope = "";
  if (difference <= 0.02 && difference >= -0.02)
  {
    newSlope = "flat";
  }
  else if(difference >= 0.02)
  {
    newSlope = "positive";
  }
  else if(difference <= -0.02)
  {
    newSlope = "negative";
  }

  // decide if a repitition was performed
  if (newSlope == "negative" && oldSlope == "positive")
  {
    if (leftInUse)
    {
      repLeftCount++;
      repLeftWeightSum += newReading;
      Serial.println("left side reading: ");
      Serial.print(newReading);
    }
    else if(rightInUse)
    {
      repRightCount++;
      repRightWeightSum += newReading;
      Serial.println("Right side reading: ");
      Serial.print(newReading);
    }
  }
  oldSlope = newSlope;

  // update maximum reading
  if (leftInUse && (leftMax < newReading))
  {
    leftMax = newReading;
  }
  else if(rightInUse && (rightMax < newReading))
  {
    rightMax = newReading;
  }
  if (exerciseMode) {
    if (currentMillis - timerMillis >= 1000) { // keep track of time passed while exercising
      exerciseTimer++;
      screen.clear();
      screen.print("Time: ");
      screen.print(exerciseTimer);
      screen.print(" seconds");
      screen.setCursor(0,1);
      screen.print("Working ");
      if (leftInUse) { screen.print("left "); }
      else { screen.print("right "); }
      screen.print("side.");
      screen.display();
      timerMillis = millis();
    }
  }
}

Schematic

Final Design DXF

]]>
Team Gary Prototype Documentation https://courses.ideate.cmu.edu/60-223/s2019/work/team-gary-prototype-documentation/ Wed, 10 Apr 2019 07:29:57 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=7291 Introduction

A few weeks ago we interviewed Gary, documented in a previous post, to understand his daily life and what type of device he may have a need for. Since our conversation, we have worked on developing a fitness device that measures differences in strength between two sides of the body. This post documents our process in developing this prototype, as well as the feedback we received on it from Gary.

Device Prototype

This device gives the user a way to quantify the strength with which they complete exercises with a resistance band.

Overall photo of works-mostly-like prototype. The modified resistance band connected to the load cell, and the OLED display where output is shown.

The load cell sensor with two hooks attached, wired to the amplifier board that sends values to the Arduino. This is our method of data collection.

We decided on an OLED display over an LCD display, for a more compact and lightweight user interface.

The prototype is straightforward to use and interact with. One should begin by attaching one of the hooks to a fixed point, such as under a door or weight bench, and attaching the other to the resistance band. Once the user turns on the device, and the screen prompts them to “Begin”, they have 10 seconds to complete repetitions of the exercise they are doing. (This length of time was chosen arbitrarily, but upon further consideration we aim to make this controlled by the user in our final product). Then, once time is up, the screen displays the maximum force exerted during that time, and there is a pause for the user to change the side they are exercising. The user can continue this process, and thus gain a quantified understanding of the difference between their two sides or between multiple repetitions.

Process Images

Notes made after meeting with Gary, sketches of initial ideas and possible features of the device we envisioned.

Early sketch of a possible physical form for the device, with possible functions for buttons and idea for attaching resistance band to load cell. We later decided to connect the load cell directly to the device, however.

A more contextualized sketch of how our device can be adapted for Gary’s needs. The load cell can be anchored either in his home gym setup or attached to doors for travel and versatility.

We cut a resistance band in half and shaped it into a loop, so that the load cell could hook onto it easily. Used heat shrink to secure the loop.

We soldered the load cell to the male headers, which eventually be connected to the load cell amplifier. The wires were extremely thin and prone to breaking, so the soldering had to be durable.

 

Testing the load cell, how different calibration values affect output. Our readings were initially precise and seemingly accurate, but were more unreliable the next time we used it. This was due to bad soldering and connections; once resoldered, the readings made more sense.

Testing the OLED display; spent time making text display in a format I wanted.

Acting out a use of the prototype by hooking the load cell underneath the table. It worked, so we decided to try and make sure the physical device could be adaptable to different surroundings.

Since Gary wasn’t able to make it to our prototype critique session, we sent him our progress over email. Soon after receiving this email, we were able to speak with Gary over FaceTime in order to show him more in depth progress and get great feedback as to what next steps to take.

Discussion

Design and hardware implementation were our primary challenges. As we began designing, we had to figure out exactly how we would anchor our project in Gary’s home and how we could achieve multiple ranges of motion with a single device. We also recognized the challenge of housing the device to be both compact and extremely durable. Finding a load cell that was the right size, could be positioned in all 3 dimensions, and could be easily interfaced with was a challenge as well. We eventually found a cell that integrated well with our Arduino, but the wires connected to the actual sensor were uncomfortably thin and prone to breaking, especially because testing required the load cell to be moved and stressed constantly.

Gary wasn’t able to make it to the crit on Monday due to a family trip to Tokyo, but we had an audio call the day after to discuss our progress. He had some questions for us regarding the use of our prototype from the pictures we sent him, but he expressed strong support for the direction we were heading in. He liked the use of a resistance band instead of an unyielding, fixed rope and the way that this allows for 3 dimensions of movement.

One idea Gary gave us that helped clarify our future plans was the distinction he made between maximum and fatigue exercises. He wants to be able to tell the maximum amount of weight he can lift with both sides of his body as well as the amount of repetitions he can do until fatigue, while our current code only tells the user the maximum force. We recognize detecting fatigue in exercise sets to mostly be a software issue, which can perhaps be addressed by taking the average force reading over multiple repetitions of the exercise. Other than this, however, Gary just reiterated that he liked our ideas; we didn’t receive any negative feedback or ideas to prod us in a different direction.

Moving forward, we have decided how we’d like the interactions between the user and the device to work, in terms of buttons and their functions on the device. Gary expressed interest in being able to quantify the strength of his weak side against that of his strong side, so we plan to include more data on the screen between sets of exercises (such as average readings, maximum readings, the readings as a percentage of past readings, etc.)

We plan to implement these ideas in significantly more detail in the next week, as well as figure out which materials and design we need to guarantee durability of our device. Once we have a more complete user interface, including buttons and the screen displaying more relevant data, we hope to also have a reasonable looks-like prototype at the end of the week. Then when Gary returns from Japan, we plan to reach out to him for more feedback on the device, and perhaps give him the opportunity to try it out in person as he couldn’t do so on Monday.

]]>
Meeting With Gary https://courses.ideate.cmu.edu/60-223/s2019/work/meeting-with-gary/ Mon, 01 Apr 2019 02:40:31 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=7033 For this project, we design and build an assistive device based on a specific older person’s needs. We held a meeting with Gary, our client, to gain some information about his life so we could find places where an assistive device could play a role. Eric, Karan, and I (Tanvi) met Gary at his office in Bakery Square on Monday, March 25, at 9:30 AM.

Meeting Agenda/Planning

We planned using a google document, the contents of which are displayed below, and decided we would mostly just let conversation flow as necessary.

DISCLAIMER: not in any particular order yet/doesn’t have to be

Introduction:

  1. Introduce ourselves: name, (what we made for project 2?), major, how long we’ve lived in/ like pittsburgh
  2. Ask Gary about why he volunteered for this project, about pittsburgh/work
  3. Explain project and goals/timeline

Main questions:

  1. Age, education/geographical experience?
  2. What did this morning look like for you? Does anything stand out to you? (anything memorable about your morning)
  3. Favorite part of the day? (what is something they enjoy doing)
  4. Why is it that you do what you do? (job wise)
  5. What motivates you to come to work?
  6. What was your favorite thing to do as a kid//something along those lines
  7. Something that is important to you? Why?
  8. What do you worry about?
  9. What is something you have wished for multiple times to change?
  10. [maybe later] what do you hope to gain from this project– basically directly asking if there is anything he thinks could be fixed

Make a mock-up of possible idea(s)? ALSO DON’T FORGET TO TAKE PICTURES

  • Make sure he has our contact info, remind him he can email us with questions or ideas if he wants at any time
  • Tanvi mostly talking, Karan mostly notes, Eric documentation/talking

Meeting Summary/Takeaways

Our discussion with Gary began with him talking about his work and education. Gary has an education in neurophysiology, and his current practice began about 35 years ago. His business grew from its beginnings in Pittsburgh to being a multi-state business. He spoke at length about his work in the operating room, taking electrophysiologic and neurologic measurements, and how he enjoyed the intellectual stimulation it gives him as well as opportunities to interact with younger people who join the business. Gary is retired, but uses his time to come into work whenever he wants and  also pursue his own hobbies, such as drawing, breadbaking, videography, and photography.

The four of us in a meeting room in Gary’s office building, Spaces. We asked him about something he least liked to do; he replied that he doesn’t really dislike any part of his life!

From there, we began talking about his experience traveling to different cities across the United States, as well as his personal vacations to other countries. Gary travels for about a week a month for work, but also enjoys traveling in his own time. For instance, we learned he would soon be visiting Japan as well as South Africa. We gathered that Gary is well-traveled, enjoys exploring, and usually has no issues during traveling time itself. We learned that Gary is interested in photography, and works on his skills as a photographer through taking pictures on his vacations. He mentioned that “the only thing is that the better cameras get heavy” and it becomes more difficult to wield them on vacations, but that high-quality cameras are unavoidably heavier because of the lens.

After we discussed Gary’s work and travel life, he suggested a few ideas of his own for devices that could help him. One was a timer for breadbaking – when he bakes multiple breads at once, he lacks a good way to keep track of all the different rise times.  Another was a device that would help him hear the what the surgeon is saying in the operating room, perhaps using a bluetooth microphone, since he has a slight hearing deficiency. However, his favorite idea is a device that would help him quantify how strong one side of his body is compared to the other side. He told us he had a stroke a year or so ago, and has no way to definitively compare the strength of one side to the other. This frustrates him as it prevents him, psychologically, from engaging in harder physical activities he has previously enjoyed (such as skiing, hiking, etc.). The physical therapist he sees compares strength by hand and “just says ‘Oh, you’re fine'”, but Gary wants to quantify this, in a sense. He said knowing how strong his weaker side is compared to his other side, as well as compared to others in his age group, would make him more comfortable returning to his active lifestyle. We spent quite a bit of time discussing this and possible forms the device could take.

Thoughts

The conversation flowed much more smoothly than we anticipated; we didn’t consult our planning document once and Gary was more than willing to talk to us about his work and personal life. He has a lot to say and is very passionate about what he does, so we were able to sit back and just listen to him talk at times. However, this did mean that we didn’t quite follow our agenda and didn’t ask all the questions we intended to. I personally wish I had asked just one more question: “What worries you?” I think (if Gary didn’t reply with “Nothing”) this would have given me a better understanding of what Gary really cares about.

Our initial impression of Gary was that he was very mentally sharp and engaged with the world around him. He seemed very comfortable with his life and mentioned that there was little about his life he didn’t enjoy, which made it somewhat difficult to find areas we could ask him to elaborate on. He seemed to realize this, though, and gave us some of his own ideas to consider. However, I feel that he did most (if not all) of the ideation during our meeting, and I wonder if our own idea for an assistive device for Gary would have been different if he hadn’t given us those ideas.

Overall, speaking with Gary was a pleasure and we all felt that the way moving forward from the meeting was clear. Gary had a conversation with Zach prior to our meeting, so he knew the ins and outs of the assignment before we stepped into his office. He knew what he wanted us to make before our conversation happened, which made our task of finding a “need” for an assistive device in his life rather easy. Because of this, our assistive device will be exactly in line with what Gary wants. However if we were to do this a second time, we would make sure to ask the important questions we had planned out, just so we could form our own impression of the situation and make the ideation more two-sided. Additionally, we were unable to meet Gary at his house as he lives in Sewickley, so we couldn’t ask him to demonstrate the exercises he had to do in his personal gym and see how he interacts with the space in his own home. Most of the information we were able to obtain from this interview was through his words and body language, and I feel that we might have learned more if we had interviewed Gary in his home.

]]>
Whack-A-Task https://courses.ideate.cmu.edu/60-223/s2019/work/whack-a-task/ Tue, 19 Mar 2019 12:46:08 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6222 Description
A box that acts as a to-do list, with a user interface comprised of buttons and an LCD screen.

Crave the satisfaction of hitting a button when you finish your homework? Do you find written planners too boring? This is the device for you!

Demo Video

Project Photos

Overall photo for proportion/scale

Welcome screen, prompting user to press taskbutton. All the changes in the system occur onscreen.

Input task function of box, controlled by bottom right button (rotary encoder). (Not sure why text is fuzzy in this post; looks fine on my computer)

Process Images and Review

My first decision point occurred when I was deciding on what the main goal of this device would be. Initially, I was considering using the device to schedule the tasks that I input, but I soon realized that using the input to schedule work time around my calendar was a deeper software task that I would have spent an inordinate amount of time on compared to the hardware portion of the project. So the device became a simpler to-do list, allowing me to focus more on things like task input.

A second decision point was made when deciding on the appearance of the device, after I had most of the electronics working. I had lasercut a clock for another class, and was considering modifying this to give the device a nicer appearance and use the hands of the clock to set a timer on each task. However, it proved tricky and time-consuming to configure a servo to receive input by manually repositioning the clock hand, so I decided to lasercut an acrylic box instead. Perhaps with more time the clock idea could have worked out, but I thought focusing on the design enough to do it justice would have detracted from the function and purpose of the device.

Idea of lasercut clock with associated parts

Process Images

Taking measurements, deciding how to design the top of the box as simply as possible

After laser cutting, realizing that some holes were not the right size and drilling them to the correct diameter

Creating an electronic prototype, working on making user input through the rotary encoder possible

Got stuck on converting the characters for task input to an array that I could index through; kept getting this strange error and couldn’t figure out what I was doing wrong. I solved this by realizing I could just cast the string to a char, so I stored the string in a char type variable. I spent way too long trying to fix this before realizing the much simpler solution.

I also spent an inordinate amount of time making sure the tasks, if they were too long for the screen, would scroll across the display. As a result, I wrote a lot of redundant code so that this would work in all situations. I eventually solved this by restructuring my code so that there were two states: one when there was a new task to be input, and the other when there was not. This way less code had to be repeated.

Discussion

On the whole, I’m satisfied with how the text and interface with the LCD screen and buttons worked out. The device met the goal of having a functioning task entry and display system. Although the device did reach its “minimum viable product” stage, I’m a little disappointed with how it came out in terms of capability and performance (it got somewhat glitchy from wires disconnecting from the Arduino) and wish I spent more time on making it robust.

At the same time, though, there would always be more I could do to make the device more useful or look nicer. I would have liked if I made the user be able to change factors such as the number of tasks (essentially the length of the task list) through the interface, or implement sound effects so that finishing a task is more rewarding. As some of my peers said, “having some sort of feedback when you complete a task would be nice”; “It would be nice to have a reward at the end!” I do agree, as this would make the device more enticing (and fun) to use. I would have also preferred the original clock idea to make it more aesthetically pleasing and implement task scheduling. If I keep working on this, I will likely implement all these smaller ideas and return to the clock design.

Through working on this project, I found that I spent a lot of time focusing on minor details and features in code, like making the tasks scroll consistently. However, I spent extra time doing this instead of considering the aesthetic aspect of the device, and the aesthetics of this project are much more lacking. I realized that I take more time to work out the kinks in my code than I need to, and should instead allocate more time to the construction of my project, as that took more time and was more troublesome than I expected. The scale was off on the laser cutter application and I printed out the box significantly larger than expected, and didn’t have enough material to retry, so I was less than satisfied with how the appearance of the box turned out. My peers also suggested that it be smaller: “It would be pretty useful as a compact handheld tool”;  “The smaller display would help keep things together.” I agree with these statements, as a task device such as this would be most useful when more desk-friendly and usable, or to carry around in person. One person said “I wish the words were lasercutted on the box so that there’s an indication of what the buttons do”; this is a solution I never thought about to make the buttons more intuitive, so the user wouldn’t need to simply figure it out.

Schematic

Code 

/*
 * Whack-a-Task: Project 2
 * 
 * Tanvi Bhargava
 * 
 * This code allows the user to press a button to display a new task 
 * on the screen, and the task scrolls across the screen if it is too 
 * long for the LCD display. On pressing another button, that which is
 * attached to the rotary encoder, it switches to "New Task" mode and 
 * allows the user to input a task by rotating the rotary encoder and
 * pressing the button on desired letters (displayed on the LCD). Then
 * a third button can be pressed to enter the task into the device,
 * and the user can select another task to be displayed on the screen.
 * 
 * For use, one must download the libraries included below.
 */


#include <Entropy.h>  // used external random library, gives different numbers even upon reset
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD screen use
#include <RotaryEncoder.h> // Library for rotary encoder position

// Declare pins
const int BUT_PIN = 2; // pin of display random task button
const int encoderA = 6; // CLK pin of Rotary Enocoder
const int encoderB = 5; // DT pin of Rotary Enocoder
const int BUT_ENTER = 7; // button pin of Rotary Enocoder
const int BUT_EXIT = 4; // third button - exit new task/mark as complete

// Declare variables used to store data
int buttonNew = 0;
bool longVal = false;
int taskIndex = 0;
int taskNumber = 7;

// variables for rotary encoder
int aState = 0;
int aLastState = 0;
int task_prev = 0;
int task_cur = 0;
bool newTask = false;
int t_prev = 0;
int t_cur = 0;
int encoder_pos = 0;
int state = 0; // when 1, is in encoder/task input state

// letters, numbers, space, characters
const int str_len = 44;
char alphabet[str_len] = "abcdefghijklmnopqrstuvwxyz0123456789 !?,.:-"; 
String message = "";
int nTasks = 10;

// create LCD display object
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Declare encoder (from library)
RotaryEncoder encoder(encoderA, encoderB);

// TASKS SETUP
// use a data structure to let each task have properties
struct task {
  String task_name;
  String due_date;
};

// Initialize tasks list - couldn't figure out how to make a dynamic array,
// not sure if that's possible, so starts off with these pre-set tasks
// and pre-set length of 10 tasks.
task my_tasks[] = {
  {"IDeATe Project", "YES"},
  {"Finish 122 programming", "Feb 28"},
  {"Eat bagels", "today"},
  {"Make buggies", "forever and ever and ever"},
  {"Prepare for interview", "March 1"},
  {"Study for midterm", "Feb 27"},
  {"SLEEP MORE", "always"},
  {"nothing", "DONE"},
  {"nothing2", "DONE"},
  {"nothing3", "DONE"}
};



void setup() {
  // pin modes
  pinMode(BUT_PIN, INPUT);
  pinMode(BUT_ENTER, INPUT);
  pinMode(BUT_EXIT, INPUT);
  
  // initialize serial, was used when debugging
  Serial.begin(9600);
  
  // initialize screen, turn on backlight, set cursor position
  lcd.init();
  lcd.backlight();
  lcd.home();
  
  // print initial messages to LCD screen
  lcd.print("Hello, Tanvi!");
  delay(1000);
  lcd.clear();

  lcd.print("Press the button");
  lcd.setCursor(0, 1);
  lcd.print("for tasks to do.");

  Entropy.initialize(); // initialize Random library

  aLastState = digitalRead(encoderA); //initialize variable for rotary encoder
}



void encoder_input() // helper function for enter task state
{
  int exit_task = digitalRead(BUT_EXIT); // read exit button's state
  int lim = 0;
  
  encoder.tick(); // line used from RotaryEncoder example
  int newPos = encoder.getPosition();
  if (encoder_pos != newPos) { // if encoder is being rotated
    lim = newPos % str_len;
    // lim is the index of the characters in the variable "alphabet"
    if (lim < 0) {
      lim += str_len; // loop around to the first index of the characters, 
      // so you don't have to move all the way back from z to a
    }
    lcd.setCursor(0, 1);
    lcd.print(message);
    lcd.print(alphabet[lim]);
    // print message and letter you're scrolling through but haven't selected
    encoder_pos = newPos;
  }

  if (digitalRead(BUT_ENTER) == 0) { // if encoder button is pressed, i.e. to select a char
    delay(500); // so you don't accidentally select the letter many times
    int lim2 = newPos % str_len;
    if (lim2 < 0) {
      lim2 += str_len;
    } // get the index of the letter being selected
    message += alphabet[lim2]; // add the selected letter to the message
    lcd.print(message);
  }
  
  if (exit_task == 1) {
    // replace the first task with a due date of "DONE" with the task just created
    // if no tasks are done, replace last task in array
    taskIndex = 0;
    while (my_tasks[taskIndex].due_date != "DONE" || taskIndex < nTasks - 1) {
      taskIndex++;
    }
    my_tasks[taskIndex] = {message, "soon"}; // didn't get around to allowing input of 
    // due date, but the process would be much the same as for a new task.
    message = "";
    newTask = !newTask;
    state = 0;
    lcd.clear(); // reset all variables used for storing message, exit enter task mode
    delay(500);
    lcd.print("Push Task Button"); // the big blue button
  }
}



void loop() {
  if (state == 0) {
    delay(200);  // need delay so doesn't change too quickly
  
    task_cur = digitalRead(BUT_ENTER);
    if (task_prev == 1 && task_cur == 0) { // when rotary encoder button is clicked
      newTask = !newTask; // change newTask state
      lcd.clear();
    }
    
    if (newTask) {
      lcd.clear();
      lcd.print("Enter a new task!");
      lcd.setCursor(0, 1);
      state = 1;
      // switch to rotary encoder mode, get input from user
    }
    task_prev = task_cur; // update button state
    
    buttonNew = digitalRead(BUT_PIN); // display different task button 
  
    if (longVal) { // if message is too long for screen
      for (int i = 0; i < 40; i++) {
        buttonNew = digitalRead(BUT_PIN); // still read button values so can exit scrolling
        if (buttonNew == 1) {
          longVal = false;
          break;
        }
        
        t_cur = digitalRead(BUT_ENTER); // scrolls until a button is hit
        if (t_prev == 1 && t_cur == 0) { // same functionality as outside the longVal loop
          // included here so you don't have to wait until it's done scrolling
          newTask = !newTask;
          longVal = false;
          lcd.clear();
          break;
        }
        t_prev = t_cur;
        
        lcd.scrollDisplayLeft();
        delay(750); // speed of scrolling
      }
    }
    
    if (buttonNew == 1) {
      taskIndex = Entropy.random(0,taskNumber); // get random task from list
      lcd.clear();
      
      lcd.print(my_tasks[taskIndex].task_name); // display the new task
      lcd.setCursor(0, 1);
      lcd.print("Due: ");
      lcd.print(my_tasks[taskIndex].due_date); // display due date
    }
    
    if (!newTask && ((my_tasks[taskIndex].task_name).length() > 16 || 
      (my_tasks[taskIndex].due_date).length() > 16)) longVal = true; 
    // if either task or due date is longer than screen of length 16, it's too long.
    else longVal = false;
  
    if (digitalRead(BUT_EXIT) == 1) { 
      // in this mode, exit button is used as "mark as complete" button
      my_tasks[taskIndex].due_date = "DONE"; // mark as done; deleting elements is too memory intensive
      lcd.clear();
      lcd.print("Good Job!");
      lcd.setCursor(0,1);
      lcd.print("Task Completed");
      delay(1000);
      // print next task on the list
      lcd.clear();
      taskIndex += 1;
      lcd.print(my_tasks[taskIndex].task_name); // display the task
      lcd.setCursor(0, 1);
      lcd.print("Due: ");
      lcd.print(my_tasks[taskIndex].due_date);
    }
  }
  else if (state == 1) {
    encoder_input(); // get user input in the "insert new task" state
  }
}

 

 

]]>
Quarter Notes https://courses.ideate.cmu.edu/60-223/s2019/work/quarter-notes/ Sat, 16 Feb 2019 06:27:25 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5738
Overall Image
LED-photoresistor setup with coin slot. The coin passing through these breaks the light to the photoresistor, triggering the flag motor.
Flag in front of ultrasonic sensor before released state. The movement of the motor raises the flag, increasing the detected distance, which triggers a speaker (left) to play a tone.

Demo Video

Basic functionality: The user drops a coin into the top of a box. This turns off an LED, which raises a small flag. This causes a short song to play, giving you a lot of satisfaction.

Progress Images and Notes

Creating the circuit on a breadboard, using an LED and covering the photo-resistor with our fingers to simulate the functionality of the speaker and coin drop. This was the phase in which we tested the different steps in our code.
Creating the box, and putting it together. Needed to get special acrylic cement, and was a time consuming process.
Building the box and placing the parts inside it, aligning them so that the coin was dropped in a precise location and the flag had space to rise. Practicality of the setup was tested and design was modified here.

Schematic

Code

/*
   Project 1
   Eric Mendez (emendez)
   Tanvi Bhargava (tanvib)
   
   Description: The user drops a coin into the top of a box. This turns off an 
   LED which is looking at a photoresistor.The light level goes below the 
   defined threshold for the photoresistor which raises a small flag. The flag 
   was covering a sonar sensor, but now the distance detected is over the 
   defined threshold which causes a short song to play, giving you
   a lot of satisfaction.
*/

#include <Servo.h>
#include <NewPing.h>
#include <TimerFreeTone.h>

// pin assignments
const int TRIGGER_PIN = 12;
const int ECHO_PIN = 11;
const int MAX_DISTANCE = 200; //max distance we want for ping
const int SERVO_PIN = 9;
const int LED_PIN = 8;
const int PHOTO_RESISTOR = A2;
const int SOUND_PIN = 7;

// declare servo and sonar from libraries
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
Servo flagMotor;

// note intervals for song
int quarter = 250;
int eighth = 125;
int sixteenth = eighth/2;
int D = 587;
int E = 659;
int F = 698;
int G = 784;
int A = 880; 
int melody[] = { D, D, A, F, G, A, G, F, E, D};
int duration[] = { (quarter+eighth), eighth, eighth, eighth, (quarter*4), 
                   sixteenth, sixteenth, eighth, (quarter*4), (quarter*4) };

// declare variables used throughout loop
int playedSong = 0;
int ledState = 1; // 1 - on | 0 - off
int lightLevel; //photoresistor
int distance;


void setup() {
  Serial.begin(9600);
  
  pinMode(LED_PIN, OUTPUT);
  pinMode(PHOTO_RESISTOR, INPUT_PULLUP); // eliminates need for pullup/pulldown resistor
  pinMode(SOUND_PIN, OUTPUT);

  flagMotor.attach(SERVO_PIN);
  flagMotor.write(90);

  digitalWrite(LED_PIN, HIGH);
  digitalWrite(SOUND_PIN, LOW);
}


void loop() {
  lightLevel = analogRead(PHOTO_RESISTOR);
  distance = sonar.ping_cm();
  Serial.println(distance);

  if (lightLevel > 200) {
    // scale reversed since using input pullup: low -> high light level
    digitalWrite(LED_PIN, LOW);
    ledState = 0;

    // throw flag up
    flagMotor.write(170);
  }

  // case over sonar detection of flag
  if (ledState == 0 && distance > 10 && playedSong == 0) {
    // flag goes up and trigger sound
    Serial.println("play sound");
    playedSong = 1;
    for (int thisNote = 0; thisNote < 10; thisNote++) {
      // Loop through the notes in the array.
      TimerFreeTone(SOUND_PIN, melody[thisNote], duration[thisNote]);
      // Play thisNote for its coresponding duration.
      delay(50); // Short delay between notes.
    }
  }
}

Discussion

The base idea for this project was formed within seconds of us starting to brainstorm. Although we continued to consider other designs for double transducers, we eventually settled on our initial idea. Our extra thinking paid off, as some of the components we decided to use were different than originally planned. This saved us some time in deciding which parts were extraneous, and it helped choose more efficient components which saved us some hassle when it came to making the project function as a whole.

While constructing the project, we found it relatively straightforward to create the circuit and write the code so that each action occurred in sequence. However, I spent some more time constructing the box in Solidworks, laser cutting it, and gluing it together. Additionally, we put effort into making the setup functional and easy to use; for example, making sure the coin always fell between the LED and photoresistor.

We also learned some interesting things about the components we were using and how they worked internally. We found that we could not use the ultrasonic rangers and a speaker together due to timing issues within the libraries they both relied on. We had to find an alternate tone library for the speaker, which was called TimerFreeTone. It was similar, but it was necessary to enter both the frequencies and beat lengths in two separate, corresponding matrices. To make the song, I looked up 12-tet note frequencies, saved them as note-named variables, and recreated the tune by looking at the note progressions online.

]]>
Smart Shoes Project https://courses.ideate.cmu.edu/60-223/s2019/work/smart-shoes-project/ Tue, 15 Jan 2019 17:51:55 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=5595 Project title: Smart Shoes (Auto-lacing and Generating Electricity)

Project creator: Maker_Inc

Description: These Smart Shoes, initally made for a science fair, generate electricity and have self-tying shoelaces. The creators integrated piezo sensors with the shoe sole to generate electricity, and used zip ties and a servo for the self-tying shoelaces.

Using power generated to power LEDs

This was an intriguing project to me as it combines sports with technology, and it’s always exciting to use gadgets while running. I like that the creators have used the potential of the shoe by using footfalls to generate electricity, while also providing information to the wearer about steps and also including a fun extra feature. I also like that the creators used relatively simple code for controlling the servo, but when coupled with zip ties, it made the self-tying shoelaces function well.

If I were to make something like this I would try to focus on one aspect (energy generation, information gathering, or another feature) so that the shoe could be more effective in a specific area. I would also try to integrate the wires and power bank with the shoe so that it’s more wearable.

Link to project: Self-Tying Shoes

]]>