Project 2 – 60-223 Work https://courses.ideate.cmu.edu/60-223/s2018/work Intro to Physical Computing: Student Work Tue, 18 Sep 2018 21:43:43 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.25 PassBox https://courses.ideate.cmu.edu/60-223/s2018/work/passbox/ https://courses.ideate.cmu.edu/60-223/s2018/work/passbox/#respond Sun, 18 Mar 2018 23:19:19 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3073

White-board box that unlocks through a 3×3 button pattern.

Decisions

A major decision I had to make was what enclosure to make for my password box. I could have gone the easy route and just purchased a box at a store, but I wanted to use the resources available through this course to create one myself, so I decided to laser cut one out of white board material. Another decision I had to make was how to make the box lock. There were difficulties because I had to implement the mechanism on the top half of the box, but the top half of the box was only a little over an inch in thickness, so it didn’t give me a lot of room to work with. I was able to mock up a mechanism that rotates a servo motor under a ledge on the lower half of the box so that when the user tries to open it while it was locked, the ledge blocks the servo motor from moving upwards which “locks” the box. When the password is entered, the servo motor rotates allowing the box to open up freely.

Self-Critique

For Project 1, I made a very simple box that lights up in one mode, and plays music in another mode and was very simple in both software and mechanically, so I wanted to challenge myself in Project 2 to make something more complicated in both of those aspects. However, during the brainstorming phase it was very difficult to come up with an idea since I wasn’t sure of any assistive devices I needed. After browsing through the different equipment in the IDeATe classroom, I discovered the 4×4 Adafruit Trellis keypad and decided to make a password box for myself to keep my items safe. I was content with how my project turned out because It was fully functional and also looked intricate because it was cut through the laser cutter. It was also sophisticated in software because I coded it from scratch from techniques I learned through class. The project also required me to learn a new piece of technology, the trellis keypad, which was difficult at first to learn how to utilize. One aspect I wish I could have improved my project in was by adding more functionality with the keypad by adding a reset password option where it allows the user to set their own unique passcode. However, because of issues with time, I was unable to fully implement that by the time of the deadline.

The trellis keypad with soldered in LEDS (Red – Password, Blue – Reset, Green – Enter)

Surprises

One aspect in my project I was concerned about after deciding on the idea was what I wanted to use for my enclosure. However, I discovered that I could create boxes using the laser cutter that don’t require any adhesive, like glue or tape, to keep it assembled. It even had a rotating mechanism on the corners that allows it to open and close smoothly, so being able to create my enclosure with a laser cutter was the most surprising aspect for me through this project, and it turned out very well. An aspect that did not work as well as expected was the mechanism I had in place for to lock the box. I had Styrofoam super-glued onto a servo motor that caused it to lock when the servo motor was rotated at a certain angle. However, it wasn’t the wisest choice to use Styrofoam cause it is not very sturdy and caused a few issues. First, when it was locked, the box would open with a little force despite being locked, and also the Styrofoam would also break if enough strength was used to try and open the box. I decided to use Styrofoam to save time, but the outcome wasn’t as expected.

Initial cardboard mock up of box

Laser-cut whiteboard pieces of final box

What I Learned

I was surprised in my ability with being able to code the passcode box within the time that it took me because this class is my first time coding in Arduino and I had no prior knowledge on how to use arrays, use modules like the trellis library, etc. However with a bit of research and prior experience with coding in Python and C, I was able to figure out how to implement more complicated methods with arrays. I had to sort the arrays so that the order that the user inputs the password does not matter. I also had to learn how to compare whether arrays were equal because there was no simple way of doing so in Arduino. Through this project I learned how to use an important data type. Something I also learned about myself through this project was how I can improve in my creativity skills. I struggled a lot through the brainstorming phase and all of my initial ideas weren’t very possible because it was too simple or too difficult mechanically to create. I was only able to think of my idea for this password box because I discovered the keypad in the classroom.

Inside of the box with the servo-motor locking mechanism shown

Next Steps

If I were to build another iteration of this project, I would first create a sturdier box by super gluing the entire box together, so it would be a lot more difficult to break into it. The purpose of the box was to keep things safe, but with the current version, a side could pretty easily be taken off, and the intruder has easy access to the belongings inside. Another improvement I would make is to replace the Styrofoam with wood to make the lock more sturdy. The Styrofoam lock can be easily broken if enough force is applied in trying to open the box. Lastly, I would also add a password reset button where the user can make their own passcode because leaving it at a default passcode would make it too easy to break into.

Schematic

Code


#include <Wire.h>
#include "Adafruit_Trellis.h"
#include <Servo.h>

#define MOMENTARY 0
#define LATCHING 1
#define MODE LATCHING

Adafruit_Trellis matrix0 = Adafruit_Trellis();
Adafruit_TrellisSet trellis =  Adafruit_TrellisSet(&matrix0);

Servo servo;
int servoPin = 3;

#define NUMTRELLIS 1
#define numKeys (NUMTRELLIS * 16)

#define INTPIN A2

int passcode[] = {0, 2, 8, 10};
int userpass[] = { -1, -1, -1, -1};
int passi = 0;
int counter = 0;
int enter = 0;

void setup() {
  Serial.begin(9600);
  pinMode(INTPIN, INPUT);
  digitalWrite(INTPIN, HIGH);

  trellis.begin(0x70);

  for (uint8_t i = 0; i < numKeys; i++) {
    if (!checknum(i)) {
      trellis.setLED(i);
      trellis.writeDisplay();
      delay(50);
    }
  }
  servo.attach(servoPin);
  servo.write(170);
}

boolean array_cmp(int *a, int *b, int len_a, int len_b) {
  int n;
  if (len_a != len_b) return False;
  for (n = 0; n < len_a; n++) if (a[n] != b[n]) return False; return True; } int sort_desc(const void *cmp1, const void *cmp2) { int a = *((int *)cmp1); int b = *((int *)cmp2); return a > b ? -1 : (a < b ? 1 : 0);
}

void printpass(int userpass[]) {
  Serial.print(userpass[0]);
  Serial.print(" ");
  Serial.print(userpass[1]);
  Serial.print(" ");
  Serial.print(userpass[2]);
  Serial.print(" ");
  Serial.print(userpass[3]);
  Serial.print(" \n");
}

void clearkeypad() {
  trellis.clrLED(0);
  trellis.clrLED(1);
  trellis.clrLED(2);
  trellis.clrLED(4);
  trellis.clrLED(5);
  trellis.clrLED(6);
  trellis.clrLED(8);
  trellis.clrLED(9);
  trellis.clrLED(10);
  trellis.writeDisplay();
}

void lightkeypad() {
  trellis.setLED(0);
  trellis.setLED(1);
  trellis.setLED(2);
  trellis.setLED(4);
  trellis.setLED(5);
  trellis.setLED(6);
  trellis.setLED(8);
  trellis.setLED(9);
  trellis.setLED(10);
  trellis.writeDisplay();
}

void resetpass(int userpass[]) {
  userpass[0] = -1;
  userpass[1] = -1;
  userpass[2] = -1;
  userpass[3] = -1;
}

int checknum(int i) {
  if (i == 0) {
    return 1;
  }
  else if (i == 1) {
    return 1;
  }
  else if (i == 2) {
    return 1;
  }
  else if (i == 4) {
    return 1;
  }
  else if (i == 5) {
    return 1;
  }
  else if (i == 6) {
    return 1;
  }
  else if (i == 8) {
    return 1;
  }
  else if (i == 9) {
    return 1;
  }
  else if (i == 10) {
    return 1;
  }
  else {
    return 0;
  }
}

int checkinarray(int i, int userpass[], int len) {
  int x;
  for (x = 0; x < len; x++) {
    if (userpass[x] == i) {
      return 1;
    }
  }
  return 0;
}

void blinkkey(int i, int j, int k, int l) {
  delay(500);
  trellis.clrLED(i);
  trellis.clrLED(j);
  trellis.clrLED(k);
  trellis.clrLED(l);
  trellis.writeDisplay();
  delay(500);
  trellis.setLED(i);
  trellis.setLED(j);
  trellis.setLED(k);
  trellis.setLED(l);
  trellis.writeDisplay();
  delay(500);
  trellis.clrLED(i);
  trellis.clrLED(j);
  trellis.clrLED(k);
  trellis.clrLED(l);
  trellis.writeDisplay();
  delay(500);
  trellis.setLED(i);
  trellis.setLED(j);
  trellis.setLED(k);
  trellis.setLED(l);
  trellis.writeDisplay();
  delay(500);
  trellis.clrLED(i);
  trellis.clrLED(j);
  trellis.clrLED(k);
  trellis.clrLED(l);
  trellis.writeDisplay();
}

void loop() {
  delay(30);

  if (trellis.readSwitches()) {
    // go through every button
    for (uint8_t i = 0; i < numKeys; i++) {
      // if it was pressed...
      if (trellis.justPressed(i)) {
        // Alternate the LED
        if (checknum(i)) {
          if (counter < 4) {
            if (!checkinarray(i, userpass, 4)) {
              userpass[passi] = i;
              passi += 1;
              counter += 1;
              if (trellis.isLED(i))
                trellis.clrLED(i);
              else
                trellis.setLED(i);
            }
          }
        }
        else if (i == 3) {
          resetpass(userpass);
          clearkeypad();
          passi = 0;
          counter = 0;
          servo.write(170);
        }
        else if (i == 11) {
          if (counter == 4) {
            enter = 1;
          }
        }
      }
    }
    // tell the trellis to set the LEDs we requested
    trellis.writeDisplay();
  }

  qsort(userpass, 4, sizeof(userpass[0]), sort_desc);
  qsort(passcode, 4, sizeof(passcode[0]), sort_desc);

  Serial.print(counter);
  Serial.print(" ");
  printpass(userpass);

  if ((counter == 4) && enter) {
    if (array_cmp(passcode, userpass, 4, 4)) {
      Serial.print("Correct");
      lightkeypad();
      enter = 0;
      servo.write(80);
    }
    else {
      Serial.print("Incorrect");
      blinkkey(userpass[0], userpass[1], userpass[2], userpass[3]);
      resetpass(userpass);
      passi = 0;
      clearkeypad();
      counter = 0;
      enter = 0;
    }
  }

}

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/passbox/feed/ 0
Assistive Device: Weather Light https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-weather-light/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-weather-light/#respond Sun, 11 Mar 2018 22:48:01 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2869 The Weather Light is a device that tracks temperature and the amount of ambient light  and changes the color of an RGB LED strip to inform the user of the current weather conditions.  For example, a high UV reading will make the light more purple, while a high temperature reading will make the light more red.

Overall View of the Weather Light

UV, Photoresistor, and Temp Sensor

Detail of the Sensor Unit

From bottom to top: the sensors, Arduino box, and light controller box

Overall scale of the Weather Light

The Process

The original idea was to have a light strip match the color of the sky.  The most difficult part of the process was figuring out how to accurately measure the brightness and color of the sky.  At first I tried using three photoresistors with a red, green and blue filter over each one.   As a quick and easy prototype,  each photoresistor was linked to an LED with its respective color.

Colored Filters Prototype

I used colored lights on my phone to calibrate the photoresistors, but the when trying this system with actual sunlight each photoresistor gave nearly identical values.  This would make for a very boring weather light that only ever changed at sunset and sunrise.

I decided to  change my original idea. Instead of displaying the color of the sky, the light-strip would change based on the weather. I added a temperature and UV sensor, and removed two of the photoresistors.  I also replaced the system of three LEDs with an RGB light-strip.

Breadboard Prototype with new sensors and LED Light strip

With the addition of the UV and temp sensors,  the Weather light took its final from.

Detail of the Sensors

Detail of the MOSFET board

 

 

 

 

 

 

Taking the breadboard design to a presentable form was fairly straightforward.  The Lightstrip was soldered to the same board as the MOSFETs. The sensors were mounted on a piece of foam-core to secure them. I laser cut three boxes: one for the sensors, one for the MOSFETs and one for the Arduino.  I also cut three pieces of wood to make a baking for the lightstrip. Because the sensor unit needs to be outside, and the lights should be able to be placed  on a wall or ceiling, I used 3 ft long cables between the boxes.

Technical Information

Schematic

Schematic

Code

int cellR = A0; 
int uvPin = A1;
int tmpPin = A2;

int redPin = 10;
int greenPin = 9;
int bluePin = 6;

void setup() {
 pinMode(cellR, INPUT); 
 pinMode(uvPin, INPUT);
 pinMode(tmpPin, INPUT);

pinMode(redPin, OUTPUT);
 pinMode(greenPin, OUTPUT);
 pinMode(bluePin, OUTPUT); 
 
 Serial.begin(9600); // starts serial communication at 9,600 baud (the rate)
}

void setColor(int red, int green, int blue)
{
 #ifdef COMMON_ANODE

#endif
 analogWrite(redPin, red);
 analogWrite(greenPin, green);
 analogWrite(bluePin, blue); 
}

void loop() {
 int valR; 
 int valUV;
 int r, g, b;

//convert voltage to farenhieght
 int rawvoltage = analogRead(tmpPin);
 float millivolts = (rawvoltage/1024.0) * 5000;
 float tmp = millivolts/10;
 
 valR = analogRead(cellR); // do the analog read and store the value and convert to rgb data
 valUV = analogRead(uvPin);
 
//Serial Prints for Debuging
 Serial.print(rawvoltage);
 Serial.println("R " + String(valR) + " UV " + String(valUV) + " TEMP " + String(tmp));
 delay(20); // slow the loop down a bit before it repeats
 
//Values are set for indoor testing use
 if (valUV &amp;gt;= 512){
 setColor(valUV/4, 0, valUV/4);
 }
 
 else if (valR &amp;lt; 200){
 setColor(valR/4, valR/4, valR/4);
 }
 else if (valR &amp;gt;= 900){
 setColor(0, 0, 0);
 } 
 else if (tmp &amp;gt;= 60){
 for (r = 0; r &amp;lt; rawvoltage/4 ; r++){
 setColor(r, 0, valUV);
 }
 for (r = rawvoltage/4; r &amp;gt; 0; r--){
 setColor(r, 0, valUV);
 }
 }

else if (tmp &amp;lt;= 20){
 for (b = 20; b &amp;lt; rawvoltage/4 ; r++){
 setColor(0, b-20, b);
 }
 for (b = rawvoltage/4; b &amp;gt; 20; r--){
 setColor(0, b-20, b);
 }
 }
}

Discussion

Although the Weather Light  was satisfactory I feel that there is more I could have done. I only used three sensors, and one LED strip. If I had had more time (and the arduino had more pins), I would have added a second or third light strip to better display weather information.  I would have also liked to have more data to calibrate, as I only was able to test the project on rainy and overcast days.

Because my initial idea was to display the color of the sky I tried to use basic image processing to get that data. I used openCV in python to try and make this work, but could not install a specific dependency for an unknown reason. I would like to try and use this software on a different project and get it working.

The light strip I used was also fairly unreliable. The color was displayed correctly on the first third of the strip, but the red channel dropped out for the rest. It would also turn off and on if it was bumped. I tried to solve this by soldering it to a board but this problem continued. Even though the light strip itself was finicky, I learned  how to use the p-channel MOSFETs to power it. I also learned what RGB values created what colors.

 

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-weather-light/feed/ 0
Assistive Device: The Waker-Upper https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-waker-upper/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-waker-upper/#respond Sun, 11 Mar 2018 15:43:42 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3093 The Waker-Upper is an assistive device that prevents students from falling asleep in class by sending vibrations when they stop writing or moving the pencil for at least five seconds.

Overall view of The Waker-Upper

Close up to potentiometer

Close up view of pencil and vibrating motors

See video of it in action here

Process

  1. The first major decision point I made was in the idea creation.  My original idea was to have a speaker wake up the student when he/she fell asleep instead of the vibrating motor.  However, I realized that this idea was implausible as it would be distracting to the people around me when using it; therefore, I had to think of new ideas to wake you up that wouldn’t be irritating to those around you.  Eventually, I settled upon the vibrating motor after testing it out and seeing how quiet it was.
  2. Another major decision point was how to account for the noise and change in x, y, and z motion that occurred when the pencil was stationary.  At first, I arbitrarily decided a number and used testing to determine if it worked.  However, this way was inefficient and made the value difficult to adjust since you would need to change it directly in the code and re-upload it to the Arduino.  Therefore, I decided to use a potentiometer, so the user could directly change the value without having to edit the code.  Using the potentiometer also made it easier to determine the value that worked best.

 

Original test for noise

In order to create this project, there was a lot of testing involved due to the variability of the accelerometer and noise in the room.  The first image is the original test I used to check for noise.  I taped a small breadboard on top of the pencil to test the range of change in the x, y, and z direction when you’re writing versus leaving the pencil stationary.  Eventually, I realized this approach was too difficult, so I switched to the potentiometer approach I described above.

Using serial feedback to debug code

This image illustrates my debugging process.  I used serial feedbacks to determine the problems with my code.  The first line is the X, Y, and Z values of motion the accelerometer picked up and the second line is the diffVal, which is set by the potentiometer and is adjusted to account for the noise.  This value allows for the user to easily adjust for noise without having to edit the code. The third line is used to determine which part of the code you’re in to see if the vibrating motors are functioning correctly.  For example, during testing, I wanted to determine if the vibrating motors weren’t turning on because of a wiring issue or a logic issue in the code and this feedback line helped me figure it out.  The fourth line is the millis and timer values, which was used to determine if the timing was working correctly.

Makeshift solution using electrical tape to keep vibrating motor attached to wire

In order to attach the vibrating motors to the wires, I needed to solder it.  However, since the wire of the motors was thin and the motors vibrated, the attachment kept becoming undone.  After re-soldering multiple times and knowing we were out of heat shrink, I used electrical tape to keep them better protected.

Completed circuit

Disscussion

  • Self-critique: Overall, I am satisfied with how my project came out.  It works as it was intended to and will succeed in waking you up if you fall asleep in class.  I can see myself using this in certain classes and even making a new one for my friend.  My only dissatisfaction was with the appearance of my project.  The box with the breadboard and Arduino is a little too big to be able to store in a classroom setting.  Also, the wires coming out of the pencil make using it in class not ideal.  In the end, I’m proud of myself for being able to bring my idea to fruition and for succeeding in making a working project.
  • Surprises : The creation of my project didn’t have too many surprises.  The biggest surprise for me was how often the motors I soldered fell off.  I must have  soldered each motor at least five times.   I was also surprised by how much harder it is to do a project alone rather than with a partner.  To finish alone, you really need to understand everything and figure things out yourself rather than relying on your partner.
  • What I learned: I learned a lot in this project.  I learned how to solder and strip wires, which I have never done before.  The project also helped me become more proficient in the use of millis for timing and setting up Boolean variables,  which I somewhat understood thanks to the homework questions.  Furthermore, the project taught me the importance of using serial feedback to debug as that was what I spent the most time doing.  I also learned about the map function and how to apply it to a potentiometer.  Overall, this project helped solidify my skills in coding along with giving me new hardware skills, boosting my  confidence in my skills.
  • Next steps: Going forward, I have more testing to do.  I would like to take my current project to one of my classes to determine how long to make the wait time between not moving the pencil and having the motors go off.  Currently, I chose five seconds, but this was only because I didn’t want my demonstration to take too long.  Also, changing the wait time may cause me to have to edit the diffVal.  I also would like to look into making the project wireless as that would look a lot cleaner and be more conducive for a class setting.   Hopefully, I could make a wireless earpiece for the vibrating motors, so wires aren’t coming out of your head as that may be seen as rude to professors.  I would also like to try to make the accelerometer wireless as having wires come out of your pencil makes it slightly harder to write.  Additionally, I would like to make the container for the breadboard and Arduino more compact, so it can easily fit in your pocket or book bag.  I may also add a switch to turn the project on and off since currently to turn it off I just unplug the battery, which is slightly inconvenient.

Technical Information

Schematic:

Code:

const int XPIN = A0;
const int YPIN = A1;
const int ZPIN = A2;
const int MOTOR_ONE = 8; 
const int MOTOR_TWO = 5;
unsigned long timer = 0;
long wait = 5000;
int previousxValue = 0;
int previousyValue = 0;
int previouszValue = 0;
int potVal = 0; 
int diffVal = 0;
bool Asleep = 0;

void setup() {
 pinMode(XPIN, INPUT);
 pinMode(YPIN, INPUT);
 pinMode(ZPIN, INPUT);
 pinMode(A3, INPUT);
 pinMode(MOTOR_ONE, OUTPUT); 
 pinMode(MOTOR_TWO, OUTPUT);
 Serial.begin(9600);
}

void loop() {
 int XPINRead = analogRead(XPIN);
 int YPINRead = analogRead(YPIN);
 int ZPINRead = analogRead(ZPIN);
 Serial.println(String(XPINRead) + " " + String(YPINRead) + " " + String(ZPINRead));
 potVal = analogRead(A3);
 diffVal = map(potVal, 0, 1023, 0, 100); 
 Serial.println("diffVal = " + String(diffVal));
 if (abs(XPINRead - previousxValue) <= diffVal && abs(YPINRead - previousyValue) <= diffVal \
 && abs(ZPINRead - previouszValue) <= diffVal){ 
   Serial.println("if statement, Asleep");
   if (Asleep == false){
    Asleep = true;
    timer = millis();
   }
 }
 else{
 Serial.println("else statement, NOT Asleep");
 Asleep = false;
 timer = 0;
 }
 if (Asleep && millis() - timer >= wait){
 digitalWrite(MOTOR_ONE, HIGH);
 digitalWrite(MOTOR_TWO, HIGH);
 Serial.println("timer = " + String(timer) + "millis = " + String(millis()));
 }
 else{
 digitalWrite(MOTOR_ONE, LOW);
 digitalWrite(MOTOR_TWO, LOW);
 }
 previousxValue = XPINRead;
 previousyValue = YPINRead;
 previouszValue = ZPINRead;
}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-waker-upper/feed/ 0
Assistive Device: Coin Bank + Laundry Timer https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-coin-bank-laundry-timer/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-coin-bank-laundry-timer/#respond Sun, 11 Mar 2018 15:03:23 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3087 Goals:

A problem I have in my everyday life is dealing with the inconvenience of my laundry situation;  I live on the third floor and the washer and dryer are in the basement.  I often find myself making multiple trips up and down the stairs only to find the laundry isn’t done, or I’ve forgotten to grab quarters.  My aim with this project was to address both of these issues.

Solution:

This project gives me a way to both store and keep track of how many quarters I have, and to monitor how long there is left in the laundry cycle.  The device can display your current balance in quarters, has an illuminated progress bar to indicate how much time is left on the laundry, and LEDs to show whether you’re timing washing or drying, and to notify you when the timer is done.  A sliding switch toggles between washing and drying modes, a button dispenses coins one at a time, and a pair of buttons sets and resets the timer.

Materials:

  1. Arduino Uno
  2. Laser cut wood housing
  3. 3x 8-way MUX
  4. 4-digit 7-segment display
  5. 10-bar LED bank
  6. 3x LED
  7. 3x push button
  8. Bump switch
  9. Sliding Potentiometer
  10. 4x 6.4v Zener diodes
  11. 5x 330 ohm resistors
  12. 4x 10k ohm resistors
  13. Servo motor

Challenges:

I faced many challenges in the design and building of this device.  The primary challenges being driving 3 7-segment digits, and finding an element way to dispense the coins.  The chief barrier that prevented the device from being ready on time though was the electrical complexity that arose from designing the solutions to the aforementioned problems.

The solution to the display problem went through several iterations before arriving at what I think is the best way to do it.  First, the idea was to use zener diodes and a lot of muxes to bounce back and forth between all the necessary inputs.  Here’s an early schematic of this design:

The obvious problem with this design I found early on was it became very, very complex very fast.  The design pictured only had 1/8 of the necessary functionality and was already hugely complex.

A second iteration used fewer muxes, and had worked off of the idea that only certain digits needed to be displayed at certain times and I could bounce back and forth between them quickly in software.  But the hardware execution of it ended up using upwards of a dozen zener diodes and the wiring would have been almost impossible to get right on a breadboard.  Also visible in the diagram below is the table used to determine the mux outputs.

The third, and final, iteration of the design was a radical departure and put much of the burden in the software to deal with the control.  It uses a system of 3 muxes which drive both the LED bank, and the 7-segment display.  The idea is that depending on what digits need to be displayed, the software will drive one segment, of one digit at a time, and rapidly bounce between them.  The first mux (top in the schematic) is used to run the LED bank, and its lower 2 outputs are the selector bits for the digit selecting mux (bottom).  The middle mux is used to select which segment is being driven in a given digit.

Schematic:

 

Notes:

  1. All resistors not labeled are 330 ohms
  2. The outputs of the middle mux are labeled corresponding to their connection to the display for clarity’s sake

Progress Images:

laser cutting the housing

initial plans for coin dispenser

Final Thoughts + code:

I think that for such a simple concept, I really underestimated the complexity of the solution.  The biggest problem for me, wasn’t necessarily coming up with a design to solve it, but actual implementation of such a complex circuit was challenging.  I spend hours and hours soldering, and redoing connections on the breadboard, and doing it all in such a small space too.  Tackling a coin dispenser or digital timer would’ve been enough of a challenge, and trying to do both I ended up getting neither working.

I waited to post the code at the bottom because of its length.  Something to note about it are the case statements which is how I cycle quickly based on the loop cycle to drive each separate segment.

Code:

const int coinSwitch = 2;
const int dispPin = 3;
const int startPin = A3;
const int resetPin = A4;
const int slider = A5;
const int washLED = 4;
const int dryLED = 5;
const int alarmLED = 6;


const int clockMuxA = 7;
const int clockMuxB = 8;
const int clockMuxC = 9;

const int barMuxA = 10;
const int barMuxB = 12;
const int barMuxC = 13;

const int servoPin = 11;

/*#######    GLOBALS   ###### */
int coinCount = 0;

long timer = 0;
bool counting = true;
bool wash = true;
const long wash_time = 1800000;
const long dry_time = 2 * wash_time;


bool prevDisp = false;
bool currDisp;

bool prevStart = false;
bool currStart;

bool prevReset = false;
bool currReset;

bool prevCoin = false;
bool currCoin;




void writeBars(){
  if (!counting) return;
  if (timer &amp;lt;= 0){
    digitalWrite(alarmLED,HIGH);
  }
  else {
    timer--;
  }

  int frac = wash ? (timer/wash_time) : (timer/dry_time);
  int numBars = frac * 5;

  //selecting on MUX which LEDs to light up
  switch (numBars) {
    case 0:
      digitalWrite(barMuxA,HIGH);
      digitalWrite(barMuxB,LOW);
      digitalWrite(barMuxC,HIGH);
      break;
    case 1:
      digitalWrite(barMuxA,LOW);
      digitalWrite(barMuxB,LOW);
      digitalWrite(barMuxC,LOW);
      break;
    case 2:
      digitalWrite(barMuxA,LOW);
      digitalWrite(barMuxB,LOW);
      digitalWrite(barMuxC,HIGH);
      break;
    case 3:
      digitalWrite(barMuxA,LOW);
      digitalWrite(barMuxB,HIGH);
      digitalWrite(barMuxC,LOW);
      break;
    case 4:
      digitalWrite(barMuxA,LOW);
      digitalWrite(barMuxB,HIGH);
      digitalWrite(barMuxC,HIGH);
      break;
    case 5:
      digitalWrite(barMuxA,HIGH);
      digitalWrite(barMuxB,LOW);
      digitalWrite(barMuxC,LOW);
      break;
  }
}


long loopCounter = 0;
void writeCount(){
  loopCounter++;
  int quarters = coinCount % 4;
  long oneMod = loopCounter % 4;
  int twoMod = loopCounter % 3;
  int threeMod = loopCounter % 5;
  int thisMod = loopCounter % 2;


  switch (quarters){
    case 0:
      digitalWrite(clockMuxA,HIGH);
      digitalWrite(clockMuxB,HIGH);
      digitalWrite(clockMuxC,LOW);
      
      if (loopCounter%2 == 0){
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW); 
      }
      else {
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      break;



    case 1:
      
      if (oneMod == 0){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW);
      }
      else if (oneMod == 1){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW);
      }
      else if (oneMod == 2){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (oneMod == 3){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      break;



    case 2:
      
      if (twoMod == 0){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (twoMod == 1){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (twoMod == 2){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW);
      }
      break;



    case 3:
      

      if (threeMod == 0){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (threeMod == 1){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (threeMod == 2){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,HIGH);
      }
      else if (threeMod == 3){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW);
      }
      else if (threeMod == 4){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,LOW);
        digitalWrite(barMuxB,LOW);
        digitalWrite(barMuxC,LOW);
      }
      break;
  }

  int dollars = coinCount / 4;
  switch (dollars) {
    case 0: 
      digitalWrite(clockMuxA,HIGH);
      digitalWrite(clockMuxB,HIGH);
      digitalWrite(clockMuxC,LOW);
      digitalWrite(barMuxA,HIGH);
      digitalWrite(barMuxB,HIGH);
      digitalWrite(barMuxC,LOW);
      break;


    case 1:
      if (threeMod == 0){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      else if (threeMod == 1){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      else if (threeMod == 2){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      else if (threeMod == 3){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      else if (threeMod == 4){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      break;


    case 2:
      

      if (thisMod == 0){
        digitalWrite(clockMuxA,HIGH);
        digitalWrite(clockMuxB,LOW);
        digitalWrite(clockMuxC,HIGH);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      else if (thisMod == 1){
        digitalWrite(clockMuxA,LOW);
        digitalWrite(clockMuxB,HIGH);
        digitalWrite(clockMuxC,LOW);
        digitalWrite(barMuxA,HIGH);
        digitalWrite(barMuxB,HIGH);
        digitalWrite(barMuxC,LOW);
      }
      break;
  }
}




void setup() {
  pinMode(coinSwitch,INPUT);
  pinMode(dispPin,INPUT);
  pinMode(startPin, INPUT);
  pinMode(resetPin,INPUT);
  pinMode(slider,INPUT);
  pinMode(washLED,OUTPUT);
  pinMode(dryLED,OUTPUT);
  pinMode(alarmLED,OUTPUT);
  pinMode(clockMuxA,OUTPUT);
}



void loop() {
  int washdry = analogRead(slider);
  wash = (washdry &amp;lt; 512);
  if (wash){
    digitalWrite(washLED,HIGH);
    digitalWrite(dryLED,LOW);
  }
  else {
    digitalWrite(washLED,LOW);
    digitalWrite(dryLED,HIGH);
  }

  //add a coin
  currCoin = digitalRead(coinSwitch);
  if (currCoin &amp;amp;&amp;amp; !prevCoin){
    coinCount++;
  }

  //dispense a coin
  currDisp = digitalRead(dispPin);
  if (currDisp &amp;amp;&amp;amp; !prevDisp){
    //move the servo up for a hot sec
    coinCount--;
  }

  //reset the timer
  currReset = digitalRead(resetPin);
  if (currReset &amp;amp;&amp;amp; !prevReset){
    counting = false;
    timer = wash ? wash_time : dry_time;
    
  }

  //start the timer
  currStart = digitalRead(startPin);
  if (currStart &amp;amp;&amp;amp; !prevStart){
    counting = true;
  }

  writeBars();
  writeCount();

}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-coin-bank-laundry-timer/feed/ 0
Assistive Device: Patience Box https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-patience-box/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-patience-box/#respond Sun, 11 Mar 2018 14:26:44 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2998 This tool detects & rewards note-taking, which also functions to mitigate binge snacking through a timed snack dispenser.

Overall View

The inside of the device. A motor controls a door that releases snacks from the top compartment. The sensor, arduino, and all the wiring are within the inside of the box.

The two buttons indicate two modes for the device – snack mode or study mode. The hole behind the box offers two options of connection to a computer for power, or connection to a power outlet.

This image displays the slot which is used to add snacks into the top compartment, which is positioned above a trap door.

The design process for the device began with the idea that my attention is often limited when studying, and that a reward or alarm system might be helpful. I was stuck for a long time, deliberating on whether I needed alarms to sound when I wasn’t studying, which sensors to use, and also if serial input would be critical.

 My initial collection of items, and one of multiple sketches involved. 

I decided to focus on making a minimum viable product, because I suffered from analysis paralysis. I decided to stop considering buzzers and lights until I was confident about the organization of my device. This is one reason why my device is made of cardboard – I needed room to experiment.

Surprisingly, it took significant consideration to decide where I wanted the trap door and snack compartment to be.

The sensor and the motor were the most essential parts of my device, so I assumed everything would naturally grow from this initial organization.

This image shows some of my experimentation with motor position; I realized that placing the motor below was much more convenient.

This image depicts part process of deciding on placement for an internal ramp.

As far as my project goes, I am not a fan of its aesthetic. It looks too shabby, but it reflects the indecision I had during the process of design. The sensor works accurately, and I am very satisfied of the sensation of the buttons – figuring out how to solder them, attach them to the roof, and wire them to the Arduino was a process. More planning and foresight would have allowed me to focus on expanding functionality, after a basic final design was settled on.

In my project, I expected some inaccuracy from the ultrasonic sensor. Initially, I thought that this was the case, but then it turned out that two wires were in the wrong location. Everything was generally as expected. However, positioning the movements of the motor was tricky. I had to test the angles at which it moved and stopped. The movements of the motor also disturbed the structure of my device as I was testing it, which made the process more vexing. The two strap-like structures were made so that I could easily open and close the device, so that I had an intuitive understanding of how to position everything within.

I mentioned at an earlier paragraph that my design was shoddy. Right now, I lack laser-cutting experience, and I may have been able to make a more appealing design with laser cut pieces. Cardboard should stick to prototyping, but with creativity, I could have made the cardboard more pleasing to the eye. Ultimately, my indecision about how I wanted the project to turn out was a significant limiting factor.

If I were to build a second iteration of the project, I would focus on laser cut pieces, or more elegant cardboard design. I’d get rid of the upper compartment, to create something visually simple. Concerns about wiring, and putting the project together caused me to use the upper compartment as a quick solution.. An ideal version would have an actual hinge for the trap door, less tape, and possibly a battery pack. Adding peizo buzzers as alarms to indicate laziness would also be in a second iteration.

Schematic

 

//author: Chileshe Otieno
//references: Robert Zacharias, 60-223 Physical Computing
#include NewPing.h;
#include Servo.h; // this allows you to use the Servo library

Servo myLittleMotor; // servo name
int servoPin = 3; // pin the servo data line is plugged into
#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 100 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
#define LIGHT 9 //Never used this, spaghetti code for potential light signal

int power = 4; // powers buttons
int yellowButton = 5; // Arduino pin connected to signal yellow button click
int blueButton = 6; //Arduino pin connected to signal blue button click
int studyDistance = 30; // number of cm used as a guide for studying activity area
long lastActivity = 0; // amount of time since last movement
long candyTimer = 5; //time between candy releases
long lastTimerValue = 0; //num seconds counted
int activeDuration = 5;

bool study_mode = false;
bool snack_mode = false;

long distanceTimer = 0; //measures last time since significant motion

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void setup() {
    Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results.
    myLittleMotor.attach(servoPin); // set up the servo on that data pin
    snackTime();
    pinMode(power, OUTPUT);
    pinMode(yellowButton, INPUT);
    pinMode(blueButton, INPUT);
    pinMode(LIGHT, OUTPUT);
}

void loop() {
    // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings.
    /*
    * delay(50);
    *Serial.print("Ping: ");
    Serial.print(sonar.ping_cm()); // Send ping, get distance in cm and print result (0 = outside set distance range)
    Serial.println("cm");*/
 
    digitalWrite(power, HIGH); 
 
    if ((digitalRead(yellowButton) == HIGH) &amp;&amp; (digitalRead(blueButton) == LOW))
    {
         Serial.println("We are studying");
         study_mode = true;
    }
    else if ((digitalRead(yellowButton)) == LOW &amp;&amp; (digitalRead(blueButton) == HIGH))
    {
        snack_mode = true;
    }
 
    if (study_mode == true)
    { 
        studyMode();
    }
    
    if (snack_mode == true)
    { 
        Serial.println("Snack Time");
        snackMode();
    }
}

void snackTime() {
 
    myLittleMotor.write(70); 
    delay(400);
    myLittleMotor.write(270);
    delay(400);
 
}

void studyMode()
{
    int dist = sonar.ping_cm();
 
    if ((millis()-lastTimerValue) &gt; 1000){ //decrements the candy timer release time by seconds

        if ((millis() - lastActivity) &lt; 1000)
        {
            candyTimer -= 1;
        }
 
        lastTimerValue = millis();
    }
 
    if ((dist &lt; studyDistance) &amp;&amp; (dist != 0)) // checks when last activity occurred
    {
 
    lastActivity = millis(); 
    }
 
 
    if ((millis() - lastActivity) &gt; activeDuration*1000) //checks if time since last activity is excessive
    {
        //alert
        digitalWrite(LIGHT, HIGH);
        Serial.println("You should be studying, dude");
        candyTimer = 5;
    }
    else if (candyTimer == 0)
    { 
        digitalWrite(LIGHT, LOW);
        snackTime();
        candyTimer = 5;
    }
 
}

void snackMode()
{
    int dist = sonar.ping_cm();
 
    if ((millis()-lastTimerValue) &gt; 1000){ //decrements the candy timer release time by seconds

        candyTimer -= 1;
        lastTimerValue = millis();
    }
 
 
    if (candyTimer == 0)
    {
        snackTime();
        candyTimer = 5;
    } 
}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-patience-box/feed/ 0
Assistive Device: The mPAD https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-mpad/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-mpad/#respond Sun, 11 Mar 2018 06:05:14 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3029

The mPAD

The mPAD is a conspicuous, intuitive personal assist device for managing and keeping track of daily tasks.

The mPAD has an inconspicuous power switch located on the side of the device to conserve power.

The mPAD can be comfortably held and operated by hand.

The intuitive user interface system is inspired by video game and cell phone controls and menus.

 

Process:

There were two main tribulations that caused fairly significant shifts in my plans when building the mPAD.  The first of these was a coding issue.  I had to create two iterations of the code for this electronic planner.  I had decided that I wanted to push my technical knowledge and skills with this project, so I tried to utilize data structures not covered in the class and offer more interactivity than is typical in many of the projects and examples shown: I wanted to go beyond having one input give one specific output, or one tool used for only one action.  I wanted something that was dynamic.  That inspired me to create an electronic device with its own graphical user interface (GUI).  I was adding, moving, and removing words, so my first iteration of the programming intuitively utilized Strings and String Arrays.  This actually worked quite well up to a point.  I was able to move strings between different arrays and I was even able to create a typing system for inserting one’s own tasks via a videogame high-score inspired letter scrolling system.  As I implemented more of the rudimentary functions of the planner, I ran into issues where the Arduino would read various arrays where I was storing tasks as empty.  The error appeared to be that I was either running out of memory, or that data was being accidently overwritten due to the large size of the Strings, which, I learned, were actually objects.  Though this obstacle was encountered quite late into the project time, I decided to rebuild my program using char*’s rather than Strings.  They took up less memory, and were easier to use for creating data structures which I hoped to use for creating a permanent memory.  I was able to create this new program by the critiques, but I did have to relinquish some interesting aspects of my project that I had hoped to implement like the videogame-esque task entering system that I had worked on the previous iteration.

The other issue that made me rethink my project was a more physical one.  I had decided that I wanted a sleek, minimalist design for the mPAD.  I wanted something that would be able to sit on my dark wooden desk at home that would be inconspicuous enough to not seem out of place among my old maps and pseudo-antiques, but also conspicuous enough for me to notice it when I sat down there after school.  I wanted something simple and ergonomic, so that the user’s (my) eyes would immediately be attracted to the screen and buttons.  I forget things, so I wanted to somewhat subconsciously force my brain to recognize the electronic box full of what I needed to do as important.  My inspiration was from the early Apple products, but with the prototype/maker culture styling that I quite enjoy.  To attain this form, I wanted to make the buttons and screen take up as much of the device as possible: perhaps a bit utilitarian in style, but effective.  I made several designs using CAD to be 3D-printed.  From past experience, I knew that printing one would likely take between four to six hours.  I put my name on the printing queue, and continued soldering wires together.  As hours passed, I remembered that many of the printers were broken and realized that if anything went wrong with the print, I might not have a housing.  It was late at that point, passed when the IDeAte lending desk was open, but I had a large sheet of acrylic from a previous project, so I decided to create a new housing that I could laser cut from it.  This involved some difficult aspects such as a backing plate to sit behind the buttons and keep them from being pushed through their mounting holes, but I was able to build it.  By the time I was able to use the 3D-printers, it was the afternoon before the critique, so I was fairly happy that I had created a backup.  Unfortunately, the acrylic was transparent.  If my design meant for the user to immediately focus on the screen, I wanted the screen and buttons to be the main source of dissonance in the design, and seeing all of the wires inside would be quite distracting.  To maintain the general homogeneity of the housing for this end and because the color would go well with my mahogany-veneered desk, I decided to leave the protective paper on the acrylic.  Though very different from my original housing design, I ended up quite pleased with how it looked.  It had the dissonance/visual interest where I wanted it at the user interface, and general cohesion/homogeneity everywhere else.

 

 

Memory and pointer errors led to strange reactions from the LCD.

The original design I modeled to be 3D-printed.

The laser cut backup housing I used due to time and resource constraints.

For testing, I used a breadboard to easily add and remove parts.

Discussion:

For this project, I took on my trouble of remembering things when I was doing something else.  There are sticky notes and phone apps out there that do this, but I always found sticky notes too inconspicuous (I’d always forget they were there) and phone apps too distracting and their lack of physical interaction unappealing.  I wanted something physical and conspicuous that was purely meant for the task of recording and reminding me what I had to do.  This project was, overall, a great learning experience, and I was able to push myself into new areas of physical computing: beyond what I could do before.  It has also, after the critiques from the last project, allowed me to explore design in my devices further.  For the design, there were aesthetic features that I wanted to add such as the minimalism and how it would fit on my desk or nightstand, but I especially wanted to focus on its ergonomics.  I wanted to create a device that a person could understand how to use it from its design.  I also wanted to create a design that would stand out to a person while they were sitting at their desk.  Overall, I was quite pleased with how my choices of size, color, button placement, and menu system turned out.  The buttons, for example, were placed in a way similar to old cell phones and arrow keys on a computer: they were arranged in a diamond with the top and bottom scrolling through the menu and the left and right buttons navigating back and forth.  I was a bit disappointed in the size of the device, however.  I was hoping for something smaller that could perhaps be put in a pocket or clipped onto one’s belt.  Unfortunately, due to the constraints of the LCD screen, the Arduino, and, particularly, the wiring, it had to be bigger.  Also, due to this, the back of the mPAD couldn’t be affixed, and I had to create a makeshift mesh to hold the wires and Arduino in.  Overall, however, the mPAD did what it was supposed to and provided a fairly intuitive and minimalist look.  I am happy with how it turned out, but I do want to continue working on it and improving it.

 

There were many surprises and challenges throughout this build.  Most notably were the times when my program indexed outside of an array and random characters and designs filled the LCD like a screen from The Matrix, but there were pleasant surprises as well.  One example of this was the backing plate that I created for the buttons on what was intended to be my backup housing.  In order to allow wires through, but prevent the buttons from slipping inside the housing, I created holes in the backing plate with two rectangular protrusions coming from opposite corners.  I wasn’t quite sure if the protrusions were strong enough or that the holes were big enough as I had already had issues fitting some buttons with solder into the face plate’s holes.  Luckily, the design worked quite well and all of the buttons and wires fit through.  Another surprise was that when trying to fit all of my soldered wires into the housing, the LCD screen stopped working.  I was able to conclude that it was likely an issue with the soldered connection to the common power supply.  As a quick test, I took a male-to-female jumper and wired the LCD to the five volt output on the Arduino and moved the common power to the 3.3 volt pin.  To my surprise everything started working again, so, rather than having to solder everything again, I used this setup for my final design.  Finally, I was surprised as to how effective the buzzer was at making deleting/completing a task.  Originally, the buzzer was something I added in just to have, but it ended up making interactions with the mPAD much more enjoyable.  It ended up being a form of positive reinforcement: when you checked off a task, the buzzer would play a nice jingle that I based off the Legend of Zelda chest opening sound.  Despite the many setbacks and features I had to remove from the mPAD, I was still surprised with how easy it was to actually use and how much I liked it.  I could use it to help me remember what I have to do as is.

 

Through this project I have gone from practically no experience soldering to having soldered my entire project together.  Along with the basics of how to solder, I have started gaining intuition and started understanding how to solder in certain situations.  One of the major problems I faced in terms of soldering for this project was the packaging of the wires.  When I created my housing, I designed it to hold the more apparent materials such as the Arduino, battery, and LCD, but neglected the wires.  I imagined that I would be able to easily compress and bend them to fit in the empty space.  I was wrong, and it led to me being unable to fit the back of my housing on.  This was a bit disappointing, but I do plan on remaking the mPAD with some new lessons I have learned: use stranded wire whenever possible, use the shortest pieces of wire you can, and plan the direction and connections of the wires so that they lay where you want them to.  Next time I might even try to implement the breadboards on which one can solder circuits together.

I have also learned a great deal about many more in depth aspects of programming the Arduino.  Most notably, as it was the main tool I used to create my program, was gaining experience with arrays and array manipulation.  Though I have used arrays using python during a computer science course freshman year and am often using MATLAB, Arduino has many more specifics that challenge how have generally used them.  In Arduino, arrays must have a fixed length and cannot be changed.  This issue led to me developing different possible workarounds to be able to move tasks around in the programs memory like I wanted.  For example, if I were to delete an element in the array I labelled as “To Do” because I had completed the task, it would leave an empty string that would be visible on the menu.  To avoid this problem I was able to make a new array that had every element after the deletion shifted over, and then reset the elements in the original array to be the same.

I also worked with the EEPROM memory on the Arduino.  This is the physical memory that remains even when the device isn’t powered.  I was able to create functions that were able to write and read character arrays to and from the EEPROM memory.  It was a very interesting challenge to tackle, and I was able to learn about creating data structures.  Unfortunately, there were some errors trying to use the characters once I read them back from the memory.  I find this a bit disappointing because it was nearly ready to be used, but due to time constraints, couldn’t.  This feature isn’t necessary, but would be extremely useful to conserve battery.

 

I am definitely planning on creating future iterations of the mPAD.  I really do believe that it would be quite useful to me in my daily life: it might actually remind me to do things that I forget.  Before I consider it complete, though, there are a few features that I think would make it something actually quite integral to my life.  Firstly, I would like to add a method to enter tasks onto the device from the device itself or the computer.  I have a method for entering it onto the device from previous coding attempts that weren’t implemented.  This would involve a system similar to videogame high score systems where one would scroll through letters to select.  I would also like to be able to enter them in from the computer.  This would require writing from the serial monitor and a change in the housing that would allow for the Arduino’s USB connection to be exposed.  It would also require another feature that I have worked on: reading and writing to the EEPROM memory on the Arduino.  Having a permanent memory that won’t disappear when the mPAD is unpowered is quite useful when it is being plugged into computers and being switched on and off.  My final addition I want to make is some sort of clock and alarm system so that one could set a time they want to complete or be reminded of a task by and it would play an alarm and show a reminder when it was that time.  I believe that these few additions could make the mPAD a useful and unique device.

 

Schematic:

Schematic of mPAD made using draw.io

 

Code:


#include &lt;Wire.h&gt;
#include &lt;LiquidCrystal_I2C.h&gt;
#include "pitches.h"
#include &lt;EEPROM.h&gt;

LiquidCrystal_I2C lcd(0x3F,16,2);

int hmPin = 8;
int frdPin = 9;
int bckPin = 10;
int nxtPin = 11;
int notePin = 7;
char* topMen [] = {"Add Task","To Do","Save","Read Test"};
int menuLen = 3;
char* tasks [] = {"Feed Dog","Do Homework","Eat Food","Save the Pres"};
int taskLen = 3;
char* toDo [4];
int toDoIn = 0;
int toDoLen = -1;
int menuPos = 0;
int up = 0;
int dwn = 0;
int nxt = 0;
int hme = 0;
int melody[] = {NOTE_A5,NOTE_AS5,NOTE_B5,NOTE_C5};
int noteDuration[] = {1000/8,1000/8,1000/8,1000/8};

void setup() {
  lcd.init();
  lcd.backlight();
  pinMode(hmPin,INPUT);
  pinMode(frdPin,INPUT);
  pinMode(bckPin,INPUT);
  pinMode(nxtPin,INPUT);
  Serial.begin(9600);
  //eepromRead();
}

void loop() {

  int nxtBt = digitalRead(nxtPin);
  int upBt = digitalRead(frdPin);
  int dwnBt = digitalRead(bckPin);

  if(!upBt){up=0;}
  if(!dwnBt){dwn=0;}
  if(!nxtBt){nxt=0;}

  lcd.setCursor(0,0);
  lcd.print(topMen[menuPos]);
  if(!up &amp;&amp; upBt)
  {
    up = 1;
    if(menuPos &lt; menuLen)
    {
      menuPos++;
    }else
    {
      menuPos = 0;
    }
    lcd.setCursor(0,0);
    lcd.print(" ");
  }else if(!dwn &amp;&amp; dwnBt)
  {
    dwn = 1;
    if(menuPos &gt; 0)
    {
      menuPos--;
    }else
    {
      menuPos = menuLen;
    }
    lcd.setCursor(0,0);
    lcd.print(" ");
  }

  if(!nxt &amp;&amp; nxtBt &amp;&amp; menuPos == 0)
  {
    delay(50);
    nxt = 1;
    addTask();
  }

  if(!nxt &amp;&amp; nxtBt &amp;&amp; menuPos == 1 &amp;&amp; toDoLen != -1)
  {
    delay(50);
    nxt = 1;
    looky();
  }

  if(!nxt &amp;&amp; nxtBt &amp;&amp; menuPos == 2)
  {
     delay(50);
     saveToDo();
  }

  if(!nxt &amp;&amp; nxtBt &amp;&amp; menuPos == 3)
  {
    delay(50);
    eepromRead();
  }
}

void addTask()
{
  up = 0;
  dwn = 0;
  nxt = 0;
  int push = 0;
  int taskPos = 0;
  int hold = digitalRead(nxtPin);
  Serial.println("I'm here");
  while(push == 0){
    int nxtBt = digitalRead(nxtPin);
    int upBt = digitalRead(frdPin);
    int dwnBt = digitalRead(bckPin);

    if(!upBt){up=0;}
    if(!dwnBt){dwn=0;}
    if(!nxtBt){nxt=0;}
    if(!nxtBt){hold = 0;}

    lcd.setCursor(0,0);
    lcd.print(tasks[taskPos]);
    if(!up &amp;&amp; upBt)
    {
      up = 1;
      if(taskPos &lt; taskLen)
      {
        taskPos++;
      }else
      {
        taskPos = 0;
      }
      lcd.setCursor(0,0);
      lcd.print(" ");
    }else if(!dwn &amp;&amp; dwnBt)
    {
      dwn = 1;
      if(taskPos &gt; 0)
      {
        taskPos--;
      }else
      {
        taskPos = taskLen;
      }
      lcd.setCursor(0,0);
      lcd.print(" ");
    }

    if(!hold &amp;&amp; !nxt &amp;&amp; nxtBt)
    {
      lcd.setCursor(0,0);
      lcd.print(" ");
      menuPos = 0;
      up = 0;
      dwn = 0;
      nxt = 1;
      hme = 0;
      push = 1;
      toDo[toDoIn] = tasks[taskPos];
      toDoIn++;
      toDoLen++;
    }
  }
}

void looky()
{
  up = 0;
  dwn = 0;
  nxt = 0;
  int toMen = 0;
  int push = 0;
  int toDoPos = 0;
  int hold = digitalRead(nxtPin);
  Serial.println("Look at me");
  while(push == 0){
    int nxtBt = digitalRead(nxtPin);
    int upBt = digitalRead(frdPin); 
    int dwnBt = digitalRead(bckPin);
    int hmeBt = digitalRead(hmPin);

    if(!upBt){up=0;}
    if(!dwnBt){dwn=0;}
    if(!nxtBt){nxt=0;}
    if(!nxtBt){hold = 0;}
    if(!hmeBt){toMen = 0;}

    lcd.setCursor(0,0);
    lcd.print(toDo[toDoPos]);
    if(!up &amp;&amp; upBt)
    {
      up = 1;
      if(toDoPos &lt; toDoLen)
      {
        toDoPos++;
      }else
      {
        toDoPos = 0;
      }
      lcd.setCursor(0,0);
      lcd.print(" ");
    }else if(!dwn &amp;&amp; dwnBt)
    {
      dwn = 1;
      if(toDoPos &gt; 0)
      {
        toDoPos--;
      }else
      {
        toDoPos = toDoLen;
      }
      lcd.setCursor(0,0);
      lcd.print(" ");
    }

    if(!toMen &amp;&amp; hmeBt)
    {
      lcd.setCursor(0,0);
      lcd.print(" ");
      menuPos = 0;
      up = 0;
      dwn = 0;
      nxt = 1;
      hme = 1;
      push = 1;
    }

    if(!hold &amp;&amp; !nxt &amp;&amp; nxtBt)
    {
      toDo[toDoPos] = ' ';
      toDoLen--;
      toDoIn--;
      char* newToDo [4];
      int newIn = 0;
      for(int tsk = 0; tsk&lt;4;tsk++)
      {
        if(toDo[tsk]!=' ')
        {
          newToDo[newIn]=toDo[tsk];
          newIn++;
          toDo[tsk] = ' ';
        }
      }
      for(int tsk = 0; tsk&lt;(newIn+1);tsk++)
      {
        if(newToDo[tsk]!=' ')
        {
          toDo[tsk]=newToDo[tsk];
        }
      }
      for(int note = 0; note&lt;4; note++)
      {
        tone(notePin,melody[note],noteDuration[note]);
        delay(100);
        noTone(notePin);
      }
      lcd.setCursor(0,0);
      lcd.print(" ");
      menuPos = 0;
      up = 0;
      dwn = 0;
      nxt = 1;
      hme = 1;
      push = 1;
    }
  }
}

void saveToDo()
{
  char nextChar = '~';
  char endChar = ';';
  int addr = 0;
  for(int tsk = 0; tsk&lt;toDoIn; tsk++)
  {
    String saveNow = toDo[tsk];
    unsigned int letters = saveNow.length();

    char charBuf[letters];
    for(int let = 0; let&lt;letters; let++)
    {
      if(addr == EEPROM.length())
      {
        addr = 0;
      }
      EEPROM.write(addr,saveNow[let]);
      Serial.println(saveNow[let]);
      addr++;
    }
    if(tsk&lt;toDoIn-1)
    {
      EEPROM.write(addr,nextChar);
      Serial.println(nextChar);
      addr++;
    }
  }
  EEPROM.write(addr,endChar);
  Serial.println(endChar);
}

void eepromRead()
{
  int listEnd = 0;
  int numTRead = 0;
  int addr = 0;
  if(EEPROM.length()!=0)
  {
    while(listEnd == 0)
    {
      char readWord[16];
      int wordEnd = 0;
      int entWord = 0;
      while(wordEnd == 0)
      {
        char val = EEPROM.read(addr);
        Serial.println(val);
        addr++;
        if(val != '~' &amp;&amp; val != ';')
        {
          readWord[entWord] = val;
          entWord++;
        }else if(val == '~')
        {
          wordEnd = 1;
        }else if(val == ';' )
        {
          wordEnd = 1;
          listEnd = 1;
        }
      }
      char* aTaskRead = readWord;
      toDo[numTRead] = aTaskRead;
      numTRead++;
      toDoLen++;
      toDoIn++;
    }
  }
}

Pitches Code:

/*************************************************
* Public Constants
*************************************************/

#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-the-mpad/feed/ 0
Assistive Device: Hydration Reminder https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-hydration-reminder/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-hydration-reminder/#respond Fri, 09 Mar 2018 23:56:54 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2858 Short description:

This assistive device reminds the user to drink water frequently, with the reminder frequency changing depending on the outside temperature.

Water bottle with hydration reminder in pouch attached with velcro strip across the middle

Close up picture to show that the magnet is not detected by the hall effect sensor when the lid is open

Close up picture to show that that when the lid is closed, the magnet will be detected by the hall effect sensor

Electronics inside the pouch

Process:

When working on my project, I had two major decision points I made.

One of them was during the initial brainstorming process, when I was trying to decide how I would allow the Arduino to identify when the user was drinking water or not. I initially had the idea of using an accelerometer, so drinking process would be defined as the tilting of the water bottle. However, considering the context of a water bottle being carried around in the user’s bag, I decided that this was an unnecessary feature. Additionally, I would still require a mechanism to indicate whether the water bottle lid is open and the user is actually drinking water. Therefore, I decided to go with the simpler option, where a hall effect sensor and a magnet attached to the lid would be the indicator for whether the user has opened the lid and is drinking water.

Picture to show when I was trying to attach the hall effect sensor and the magnet to the water bottle. Exact positioning was crucial to ensure interaction between the magnet and the sensor.

Another pivot point I encountered was when I was trying to attach all the electronics to the water bottle, when I realized that with the Arduino, 9V battery for power, and the breadboard with all sensors wired in, it was becoming too bulky for the user to carry around. Moreover, none of the electronics were waterproof, and would be problematic if there was water dripping down the side of the bottle. Based on these observations, I decided to first of all remove the breadboard, soldering the wires to the sensors and wiring them directly into the Arduino pins. This allowed me to decrease the amount of space that the appliances occupy significantly. I also made the decision to sew together a nylon pouch to contain the electronics, for aesthetic reasons, and since nylon is water resistant. With the 9V battery and the velcro strips attached, this allows the user to use the device for any water bottle of their liking.

Picture to show exposed electronics on the water bottle. This would have been problematic since water may be dripping down the side, and would be harder for the user to hold.

Picture to show how the electronics were wired after removing the breadboard and soldering everything together.

Discussion:

In terms of satisfying the goal of creating an assistive device for someone, I think I was able to succeed. Hydration is an action that is so vital for our well-being, but many people tend to ignore its importance. By creating a device to assist better health, especially for people living in hotter climates, I believe I was able to achieve the goal of assisting someone’s life. However, I believe the technical performance of the device could be further improved, considering how simple the mechanism is. This was what surprised me when working on the project. On testing day, the hall effect sensor fired up like crazy without a magnet in near proximity because the resistor between the power and the output broke off. It reminded me that even one resistor could affect how the device works, as the sensors are sensitive objects. Also I found difficulties positioning the hall effect sensor near the lid and the magnet on the lid, so the device would operate with a high reliability.

One thing I did not realize until later on was the complication related to soldering. As mentioned earlier in my second pivot point, I wanted to have everything of my device contained in the pouch, so the user can easily attach and detach the device. In order to do this, I got rid of the breadboard and soldered all resistors onto the wires and the sensors together. However, the joints would become weak, susceptible to outside forces, with a higher chance of the wires snapping. Also, the chance of the power/output/ground wires touching increased since the wires were exposed, so I had to put electrical tape around every metal part.

Through this project, I learnt that coding and debugging was not too big of an issue, as the concept of tracking the time using millis() and different states, was very similar to the problems we attempted in class. However, I would like to work on improving the aesthetics of my project, as a clean exterior would ensure that the mechanism is more user friendly, and possibly reduce complications related to exposed electronics.

This also applies to the following steps I would like to take if I were to improve this project further. First of all, I would like to explore different mechanisms for detecting when the water bottle is open or not, so I can have all electronics contained in minimum space. This ensures an improved exterior for both aesthetics and usability. I would also want to look at different soldering techniques to make sure that the wires will be reliably intact even without the breadboard.

Technical Information

Schematic – Drawn using Fritzing

Code

unsigned long lastOpened = 0;
unsigned long threshold = 0;
const unsigned long thresh1 = 30ul * 60ul * 1000ul;
const unsigned long thresh2 = 60ul * 60ul * 1000ul;
const unsigned long thresh3 = 90ul * 60ul * 1000ul;
int MAGNETPIN = A0;
int TEMPPIN = A1;
int BUZZERPIN = 8;
int lidState = 1; //0 is closed, 1 is open
int lastLidState = 0;
bool buzzerState = LOW;

void setup() {
 pinMode(MAGNETPIN, INPUT);
 pinMode(TEMPPIN, INPUT);
 pinMode(BUZZERPIN, OUTPUT);
 Serial.begin(9600);
}

void loop() {
 int lidState = digitalRead(MAGNETPIN);
 float tempv = analogRead(TEMPPIN);
 float temp = (tempv*5000/1024-500)/10; //in celcius
 //set different threshold for time closed depending on outside temp
 if (temp >= 30){
    threshold = thresh1;
 }
 else if (temp >= 20 && temp < 30){
    threshold = thresh2;
 }
 else if (temp < 20){
    threshold = thresh3;
 }
 if (lidState == 0){
    //water bottle is closed
    if (lidState != lastLidState){
       //just closed
       lastOpened = millis();
    }
    if ((millis() - lastOpened) > threshold){
       //Water bottle has not been opened for too long
       buzzerState = HIGH;
    }
 }
 else if (lidState == 1){
    //water bottle is open
    buzzerState = LOW;
 }
 digitalWrite(BUZZERPIN, buzzerState);
 lastLidState = lidState;
}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-hydration-reminder/feed/ 0
Assistive device: CHAIRninja https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-chairninja/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-chairninja/#respond Thu, 08 Mar 2018 23:04:18 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2811 Get up or get embarrassed! A chair that ensures you take a break every 30 minutes you sit.

 

CHAIRninja

I am guilty of sitting for long hours in front of my computer when I am working, and this is bad. Medical experts recommend taking a break every 30minutes by standing up or walking around. While I know this, it is easy to ignore while I am deep into my work. Therefore, I decided to outfit my chair with an assistive device that not only reminds but embarrasses me out of my chair.

(Sorry about the vertical video).

 

The CHIARninja

 

Materials Used + How it works

The sensors used to detect whether someone is sitting or not:

  • Square FSR (force sensitive resistor)
  • Ultrasonic ranger

While the initial plan was to use only the force sensitive resistor on the back of the chair to detect if a person is sitting on the chair or not, it is possible that the person is sitting but without their back resting on the back of the chair. I, therefore, decided to use the FSR on the seat and the ultrasonic ranger on the back of the chair so the sensors can together detect even if someone is sitting on the edge of the seat.

Ninja eyes, the ultrasonic ranger

To provide feedback to the person sitting:

  • Vibration motor (to warn the person sitting that it is time to get up)
  • Piezo buzzer (will buzz if the person does not get up even after the vibration motor goes off)

Initially, the piezo buzzer was supposed to go off at the end of 30 minutes of sitting, but I decided to add a vibration motor as a warning before the piezo buzzer is activated. This will vibrate for 20seconds and the piezo buzzer will turn on only if the person still does not get up. The vibration as well as the piezo buzzer (if it has turned on) will stop as soon as the person gets up from the chair. However, they will turn on again if the person sits again within 5 minutes of getting up.

The FSR and the vibration motor

Other items:

  • Arduino Uno
  • Small Breadboard
  • 9V Battery
  • Jumper wires
  • Cardboard (or any material to make a box), Glue, Tape and other supplies

All the parts coming together in a foam core box

The decision to design a box with clear windows was because I love the messy and colorful wires and other electronic parts. I wanted people to be able to see all of it.
It also allows me to see if all the wires are in place or if something has come loose.

Inside the foam core box with windows

 

The electronics mounted onto the chair

The CHAIRninja in all its glory!

 

Schematic

(TBD)

 

The code

____________________________________________________________________________

</pre>
<pre>#include <NewPing.h>

unsigned long timer = 0;

int buzzerPin = 6;
int vibratePin = 8;
int triggerPin = 12;
int echoPin = 11;
int fsrPin = 0;
int fsrReading; // the analog reading from the FSR resistor divider
int sonarReading;

NewPing sonar(triggerPin, echoPin);

bool history [30] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int x = 0;

//standing = 0
//sitting = 1

void setup() {
 Serial.begin(115200);
 pinMode(buzzerPin, OUTPUT);
 pinMode(vibratePin, OUTPUT);
 pinMode(fsrPin, INPUT);
 pinMode(triggerPin, OUTPUT);
 pinMode(echoPin, INPUT);
}

void loop() {
 fsrReading = analogRead(fsrPin);
 sonarReading = sonar.ping_cm();

 if (millis() - timer >= 1000) {

 history[x] = (fsrReading >= 100) || (sonarReading < 40);
 Serial.println(fsrReading);
 Serial.println(sonarReading);

 Serial.println("fsrReading = " + String(fsrReading) + "; sonarReading = " + String(sonarReading));

 x++;

 if (x == 30) {
 x = 0;
 }

 timer = millis();

}

int sum = 0;

 for (int i = 0; i < 30; i++) {
 sum = sum + history[i];

}


 if (sum == 30) {
 digitalWrite(vibratePin, HIGH);
 delay(3000);
 digitalWrite(vibratePin, LOW);

 if ((analogRead(fsrPin) >= 100) || (sonar.ping_cm() < 40)) {
 tone(buzzerPin, 1000);
 }

}

else if (sum < 30) {
digitalWrite(vibratePin, LOW);
noTone(buzzerPin);
}

}</pre>
<pre>

____________________________________________________________________________

While I had initially thought I will primarily be using millis( ) to achieve the behavior, I ended up having to code this differently (with massive help from Zach). We created an array which stored 30 values and the ‘if’ condition populated these values with 0 and 1 (0 for standing and 1 for sitting). If the  sum of all the values in the array exceeded 29, then the vibration motor is activated followed by the piezo buzzer.

Learning to code (I’m still really bad at it) has been a big thing for me as part of this course. While I understand the concept, remembering to write the exact code is always a challenge.

 

The electronics inside the box

 

Reflection + Next steps

Ideally, I would have loved to integrate this in a chair that has foam seats and back. This way, I could have hid all the electronics within the chair’s structure. Also, I need to give additional thought to the location of the vibration motor (instead of sitting on it).

I would love to explore Velostat, a material that behaves like an FSR. This way, I can cover the seat of a chair with velostat making the electronics seamless and not even need the second sensor (ultrasonic ranger) as the velostat can detect a person anywhere on the seat.

Another thought I had was how the chair could detect if it is a human sitting on the chair or some object kept on it. Currently, there is no way of detecting that. I could probably use some kind of RFID sensor that talks to something in my back pocket.

CHAIRninja

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-chairninja/feed/ 0
Assistive Device: Slouch Detector https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-slouch-detector/ https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-slouch-detector/#respond Thu, 08 Mar 2018 07:30:20 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2778 This slouch detector will calibrate to fit your individual posture and will alert you with an LED and on-screen text when you are slouching.

The detector was attached to an XL mens shirt

Volunteer using the detector

The bottom of the sensor sewn with conductive thread to the shirt

Button snaps were sewn onto the conductive fabric and a shirt to allow for easy access when adjusting the project.

 

Process:

My original idea was to create a conductive stretch sensor that would sit either perpendicular or parallel with the spine and the slouching movement would stretch the yarn, thus completing the circuit.

First setup: crocheted thick yarn (purple) with conductive thread used with an ohmmeter to test resistance readings and complete circuitry.

I knew one design decision I had to make was which nonconductive yarn to use so I started with a thick yarn and conductive thread because the thread was the only conductive material I had that could be incorporated into a stretchy fabric, and the thick yarn would provide resistance so that the circuit wasn’t always on (only when it was stretched).

Crochet chain with thick yarn (purple) and conductive thread (grey)

 

Testing of knit I-cord made with thin yarn (pink) and conductive yarn (grey).

I realized that the type of stitch I used would also make a difference because more “entangled” stitches would allow for more contact of the conductive yarn and also more stretch to the chain. The I-cord is an example of a stretchy, “entangled” stitch because it is knit around in a circle, providing many contact points for the yarn.

K1P1 (knit 1, purl 1) rib stitch with thin yarn (pink) and conductive yarn (grey)

Sweaters typically feature a knit 1 purl 1 rib stitch which is very stretchy so I decided to try it because the I-cord wasn’t responding well to stretches (no clear distinction in the readings). I tested samples by waiting for the readings to stabilize (shown below), and then stretching and relaxing them at a constant rate to see if the readings could pick up the stretching.

Steady state readings for K1P1 thin yarn and conductive yarn

An example of readings with no clear distinction between stretching and relaxing:

Readings for crocheted chain of thin yarn and conductive yarn

Example of decent readings:

Readings for crocheted chain of conductive yarn

It seemed that the I-cord was too entangled to get good readings, so it was narrowed down to the crocheted chain and K1P1 rib stitch. I then progressed to using the conductive yarn by itself because the readings of nonconductive yarn + conductive yarn had a lot of noise in them.

K1P1 rib stitch with conductive yarn

Crochet chain with conductive yarn

Human trials: crocheted chain of conductive yarn

Although the conductive yarn picked up the distinction between stretched and relaxed, it was very sensitive in that the readings would only have a range of ~80. This was not a large enough range considering that the steady state wasn’t 100% stable. I had other nonconductive yarn available as well as many other stitches to try, I had to decide if I wanted to go through with it or move on so I decided to try it on myself to see if it had some potential. It performed worse than when I was manually stretching it so it was time to try a sewn pressure sensor.

How the pressure sensor was made: Conductive fabric sewn to jersey knit as the bread, with velostat as the lunch meat. Note that the velostat is not sewn in to prevent the conductive fabric from touching all the time.

The pressure sensor was pretty stable at steady state and could distinguish between stretched and relaxed with a range of ~200, so this was the best solution by far. Thus the final design decision was to let go of the stretch sensor and proceed with the pressure sensor. Since the readings still fluctuate a little, I had to adjust my code to take a number of readings and average them to get a reliable output.

Human trials: pressure sensor prototype

 

Discussion:

I am happy with the way the project came out because the slouch detector is quite responsive and works well. However, the wires are annoying so while I am glad the shirt allows for more movement, I wish I was able to self contain the entire project so that it could work with any shirt and wouldn’t be attached to anything else (like the computer). I am also a little peeved I didn’t know about a flex sensor because although making my own sensor was fun, I could have used the time I spent on the stretch sensor on the project housing instead.

I was surprised that the conductive yarn by itself worked better than with a nonconductive material, because I expected the conductive yarn to be too sensitive and never stabilize. Although the project works, completing it took so long that even if it didn’t work, the amount of effort I put in would encourage me to just remind myself to sit up straight. Or maybe not, because bad habits are hard to break and I’m not sure I’ll actively use my project until I have full blown back problems in 10 years.

I found that the hardest part of this project was actually implementing my idea. When I imagined my project, there were no wires so actually building the project was harder than I expected because I had to put in the wires and try to hide them. So I learned that I like to see the big picture, which is helpful for keeping in mind the end goal but it isn’t helpful for going over the nitty gritty details. If I had to do it again, I would tell myself not to get so hung up on perfecting details because I spent a lot of time experimenting with the stretch sensor and was almost going to use a design of experiments in order to test the statistical importance of the different factors (yarn type, stitch, conductive thread vs yarn). Although I already had the idea to use the pressure sensor, the thought of sewing it together put me off because I thought I would waste a lot of time restarting my project. However, I was only making little improvements with the stretch sensor and trying more experiments didn’t seem like it would make it work perfectly so ultimately the time constraint was what made me try the pressure sensor, which worked on the first try! I found that putting it together only took about 30 minutes once I found a fabric to sew the sensor onto so I should have let go of the stretch sensor sooner.

I want to build another iteration of this project, but I’d probably scrap what I already have and either create a new sensor or use a flex sensor just so that it is more reliable. The current iteration has to be adjusted every 10 minutes because the velostat likes to slide down the sensor, allowing the top part of the conductive fabric to touch. I’d also have to come up with another way to attach it to me because I don’t like that it only works with one shirt. The shirt probably won’t stand up to a wash so this iteration was only good for a demonstration. In addition, I would add positive or negative reinforcement so that other people who need more than just a reminder to fix their posture can also use it.

Diagram of circuit

Code:

int r = 7;
int startup = 0;
int LED = 2;
unsigned int slouchTotal;
unsigned int straightTotal; 
unsigned int slouch; //avg "slouch" reading
unsigned int straight; //avg "sitting straight" reading
const int numReadings = 20; //number of readings to get an accurate avg
unsigned int slouching[numReadings]; 
unsigned int sittingStraight[numReadings];
int slouchIndex = 0;
int straightIndex = 0;

void setup() {
 pinMode(A0, INPUT);
 pinMode(r, INPUT);
 pinMode(LED, OUTPUT);
 Serial.begin(9600);
 for (int i = 0; i < numReadings; i++){
 slouching[i] = 0;
 sittingStraight[i] = 0;
 }
 Serial.println("hello! in 3 seconds, you will be asked to slouch");
 Serial.println("press the button if you want to reset!");
 delay(3000);
}

void resetSlouch(int foo){
 slouching[slouchIndex] = foo;
 slouchTotal += foo;
 //only determine avg if more than one reading has been made
 if (slouchIndex != 0){
 slouch = slouchTotal/slouchIndex;
 }
 if (slouchIndex == numReadings-1){
 startup ++;
 Serial.println("done calibrating your slouch! in 3 seconds, you will be asked to sit up straight");
 delay(3000);
 }
 slouchIndex ++;
}

void resetStraight(int foo){
 sittingStraight[straightIndex] = foo;
 straightTotal += foo;
 //only determine avg if more than one reading has been made
 if (straightIndex != 0){
 straight = straightTotal/straightIndex;
 }
 if (straightIndex == numReadings-1){
 startup ++;
 Serial.println("done calibrating your good posture!");
 delay(1000);
 }
 straightIndex ++;
}

void loop() {
 if (digitalRead(r)){
 Serial.println("resetting");
 Serial.println("you will be asked to slouch in 3 seconds");
 startup = 0;
 slouchIndex = 0;
 straightIndex = 0;
 //give time for person to respond
 delay(3000);
 }
 
 //calibrate slouching
 if (startup == 0 && slouchIndex < numReadings){
 Serial.println("please slouch...");
 resetSlouch(analogRead(A0));
 }
 
 //calibrate sitting straight only after slouching is calibrated
 if (startup == 1 && slouchIndex == numReadings){
 Serial.println("please sit up straight...");
 delay(200);
 resetStraight(analogRead(A0));
 }
 
 //done calibrating so check posture
 else if (startup == 2) {
 if (analogRead(A0) >= (slouch+straight)/2){
 //Serial.print(slouch); Serial.print(straight);
 digitalWrite(LED, HIGH);
 Serial.println("stop slouching!!!!!!!!");
 }
 else{
 digitalWrite(LED, LOW);
 }
 }
 
 delay(500);
 Serial.println(analogRead(A0));
 
}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/assistive-device-slouch-detector/feed/ 0
Assistive Device for Self: Noise Nudger https://courses.ideate.cmu.edu/60-223/s2018/work/surprise-the-light-bulb-2/ https://courses.ideate.cmu.edu/60-223/s2018/work/surprise-the-light-bulb-2/#respond Wed, 07 Mar 2018 23:20:24 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=2698 What I’ve Made?

I created a device that allows me and other students to anonymously silence loud talkers in our design studio space.

How Does it Work?

The device prototype is comprised of a noise nudging station and a whistle-shaped notifier. The station has a button and a microphone, and ideally will be located on every student’s desk. The notifier has an Arduino, a battery, a DFPlayer Mini MP3 Player, a speaker and two Neo-pixel LEDs. Ideally the notifier will be located at the center of the studio space, hanging from the ceiling.

If the user is pressing the station button and the noise level (reading from microphone) is below certain threshold, the notifier will not be activated since the environment is judged to be not that noisy.

If the user is pressing the station button and the noise level is above certain threshold, the notifier will play “Please keep your voice down”recording for three times, and the LEDs will light up with an animated color pattern.

Hardware-Notifier

Hardware-Noise Nudging Station

Top View

Electronics Wiring Diagram

The Arduino code for controlling the system is shown below:

</pre>
#include "Adafruit_NeoPixel.h"
//#include "Arduino.h"
#include "SoftwareSerial.h"
//#include "DFRobotDFPlayerMini.h"
#include <DFMiniMp3.h>

#ifdef __AVR__
#include <avr/power.h>
#endif

#define PIN1 6
#define NUMPIXELS1 32
Adafruit_NeoPixel ring1 = Adafruit_NeoPixel(NUMPIXELS1, PIN1, NEO_GRB + NEO_KHZ800);

int delayval = 500; // delay for half a second

class Mp3Notify
{
public:
static void OnError(uint16_t errorCode)
{
// see DfMp3_Error for code meaning
Serial.println();
Serial.print("Com Error ");
Serial.println(errorCode);
}

static void OnPlayFinished(uint16_t globalTrack)
{
Serial.println();
Serial.print("Play finished for #");
Serial.println(globalTrack);
}

static void OnCardOnline(uint16_t code)
{
Serial.println();
Serial.print("Card online ");
Serial.println(code);
}

static void OnCardInserted(uint16_t code)
{
Serial.println();
Serial.print("Card inserted ");
Serial.println(code);
}

static void OnCardRemoved(uint16_t code)
{
Serial.println();
Serial.print("Card removed ");
Serial.println(code);
}
};

SoftwareSerial secondarySerial(10, 11); // RX, TX
DFMiniMp3<SoftwareSerial, Mp3Notify> mp3(secondarySerial);

int noisePin = A0;
int buttonPin = 2;
int buttonState = 0;
int noiseReading = 0;
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
long timer = 0;
int previousCounter = 0;
int Counter = 1;

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

Serial.println("initializing...");

pinMode(noisePin, INPUT);
pinMode(buttonPin, INPUT);

ring1.begin();
ring1.show();

mp3.begin();
mp3.setVolume(30);

Serial.println("starting...");

}

void loop()
{

long startMillis = millis(); // Start of sample window
int peakToPeak = 0; // peak-to-peak level
int signalMax = 0;
int signalMin = 1024;

while (millis() - startMillis < sampleWindow)
{
noiseReading = analogRead(noisePin);
if (noiseReading < 1024) // toss out spurious readings
{
if (noiseReading > signalMax)
{
signalMax = noiseReading; // save just the max levels
}
else if (noiseReading < signalMin)
{
signalMin = noiseReading; // save just the min levels
}
}
}
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
Serial.print ("Volume: ");
Serial.print (peakToPeak);

buttonState = digitalRead(buttonPin);
Serial.print ("; Button: ");
Serial.print (buttonState);

Serial.print ("; Timer: ");
Serial.println (millis() - timer);

if ( peakToPeak >= 45 && buttonState == HIGH ) {
mp3.playRandomTrackFromAll();
previousCounter = Counter;
mp3.pause();
}

if (previousCounter == Counter) {
for (int i = 0; i < 10; i++) {
mp3.start();
delay(1000);
mp3.pause();
for (int j = 0; j < NUMPIXELS1; j++) {
ring1.setPixelColor(j, ring1.Color(255, 20 * i, 0));
}
ring1.show();
}
previousCounter = 0;
} else {
for (int i = 0; i < NUMPIXELS1; i++) {
ring1.setPixelColor(i, ring1.Color(0, 0, 0));
}
ring1.show();
}

delay( 100 );
}
<pre>

The code can also be found via the link below:

https://github.com/willowhong/Noise-Nudger/blob/master/Nudger.ino

What Is the Process?

1. Firstly I tried to make the button, the microphone, the DFPlayer, the speaker and one NeoPixel work together. I eventually found one DFPlayer library that works well.

2. Then I put two NeoPixels into a series circuit, and found that the player is not functioning well. I then learnt that the LEDs need a separate 5V power to avoid interference with the player.

3. Next I lasercutted the whistle form and put all parts together for testing. I found that the width and the place for tiding the string were not right. And the glue trace was too obvious on the transparent acrylic.

4. Lastly I recutted the form and tested all parts again.

REFLECTION

I spent a lot of time trying to figure out how to make the audio play and the NeoPixel light animation work together. Initially I hooked up everything and ran the code, and I could not figure out why when there was no output. I then learnt to do one thing at a time, trying to make the simplest system work first, then adding more parts to increase the system complexity.

Another thing I learnt is that I need to connect everything to one common ground. Initially I connected the player’s GND to Arduino’s GND pin, and connected the NeoPixel’s GND to external power’s GND. This results in a different reference point for reading voltages (signals). In order to have a correct signal reading, all grounds should be connected together.

 

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/surprise-the-light-bulb-2/feed/ 0