ssyeda@andrew.cmu.edu – Intro to Physical Computing: Student Work Spring 2022 https://courses.ideate.cmu.edu/60-223/s2022/work Intro to Physical Computing: Student Work Sat, 07 May 2022 17:21:35 +0000 en-US hourly 1 https://wordpress.org/?v=5.8.9 Inspiration Capture Device by Team Callisto: final documentation https://courses.ideate.cmu.edu/60-223/s2022/work/inspiration-capture-device-by-team-callisto-final-documentation/ Fri, 06 May 2022 15:38:46 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15849 Inspiration Capture Device by Team Callisto: final documentation

The Inspiration Capture Device (ICD) is the product of weeks of collaboration with our team’s client, Mary. After meeting with Mary multiple times in person and through the phone, we determined that Mary, as a writer, values the inspiration that can come at a moment’s notice highly. Thus, we wanted to make her an assistive device that allowed her to quickly and easily capture whatever kind of inspiration reaches her in her daily life so that it could help her writing process.  For some more background information on our project, please refer to this post: https://courses.ideate.cmu.edu/60-223/s2022/work/team-callisto-interview-documentations/

What we built

The ICD captures ‘inspiration’ in two different ways- through a picture taken on a small camera or through a voice memo recorded on an audio recorder. The ICD is meant to do this while being as accessible as possible for Mary; the device is split into two parts (a remote controller and a necklace) to make it easy for her to interact with. At the mere press of a button, Mary can take a picture of whatever she is currently facing or turn on a voice recorder on her neck to quickly capture what’s on her mind for future use.

Camera component of the ICD’s remote controller

 

Inside the remote you’ll notice various wires as well as an Arduino Uno and a transceiver on the left near the camera

 

Top view of the remote controller, featuring two buttons with their LED lights meant to indicate current remote activity

 

The necklace part of the ICD featuring a green LED

 

The above video is a compilation of clips relating to the ICD’s capabilities.

Image of Juan’s hand that was captured in the ICD demo video.

Narrative

While Mary is out shopping, she notices that a beautiful unfamiliar kind of bird has perched itself on a table in front of her. She pushes the ICD’s red button to take a picture of the bird so she could find out what type of bird it is later. After doing so, she decides to write something about the bird.

Later in the week, she suddenly gets a fantastic idea for a short story  while she’s out and about in Pittsburgh. To ensure she doesn’t forget this idea, she presses the ICD’s blue button and says her idea out loud. She then presses the button again to stop and save the recording. Hours later, Mary uploads the file on her computer, listens to her voice memo, and starts getting to work on an amazing story.

Prototype and Process

Our prototype was broken into four parts so that we could test each component before combining them.

The four components included the remote control, the microphone, the camera, and the necklace. The remote control included large buttons connected to LEDs and wireless transmitters, the microphone and the camera were each attached to an SD card, and the necklace included a casing for internal components and a chain.

The remote control component of the ICD prototype

 

The microphone component of the ICD prototype

 

The camera component of the ICD prototype

 

The necklace component of the ICD prototype

 

The above video showcases the ICD’s simple user interface.

Prototyping

The initial sketches for the necklace part of the ICD

Through our interview with Mary, we found that she is creative and a writer but cannot write down her thoughts quickly, so we created a solution that would allow her to record herself the moment she receives inspiration.

The initial sketch for the remote control part of the ICD

After talking with Mary about our initial sketches and solutions, she suggested that we also include a camera capability because she needs the help of someone else to use her phone camera. We also decided to create a separate remote that housed the camera but also triggered the microphone in the necklace wirelessly to make it more accessible for Mary.

The initial sketch for the attachment of remote

We were trying to figure out the best way to attach the remote to the wheelchair so that it would be easy for Mary to press to record her life. We found that it might be best for remote attachment works like a watch to fix on the wheelchair controller.

 

Started a simple setup to work on the microphone component using a microsd card reader module and a microphone module

Upon recording the audio noticed that there was a lot of noise. In order to investigate where the noise was coming from we hooked the output of the microphone to an oscilloscope. With some testing we realized that the noise was coming from writing to the SD card reader.

During the prototype critique, we asked Mary to interact with the microphone set up, focusing on how she would insert and remove the microsd card from the reader. We found that reader module was not optimized for Mary’s hands as the sd card was too small and the push mechanism to remove was difficult. And so, we invested in a larger SD card reader.

Unfortunately, the noise still persisted. We switched the arduino uno for an arduino pro micro to see if the board made any difference. Our final design would also use a pro micro as it would allow the microphone to fit in the necklace holder.

The above change did not fix the noise either. As a last resort we scrapped the SD card readers and invested in a module(vs1053)  that had the optimizations required to cleanly record audio. Given more time we would have fully recorded audio with little noise, but instead the audio files were written to the SD card without any audio.

There were also some issues with other components; a moment of frustration regarding the transceivers in our project should be mentioned: Juan spent an unideal amount of time attempting to debug issues with the wireless transceivers. Unsure if the issue was hardware, software, or wiring-related, hours were lost on his part trying to fix the transceiver problem and could’ve gone towards other aspects of the project. This narrow focus on solving the problem in retrospect was a mistake- the time spent trying to resolve the issue (which ended up being due to a broken transceiver) could have instead been spent assisting Sumayya with the voice recorder component of the project. By the time the solution to the transceiver problem was discovered, it was simply too late to assist with other aspects of the project. This served as a learning experience regarding debugging physical computing components as well as knowing when to cut your losses. In addition, we also had some issues with the ICD’s physical case.

The failed versions of the cases

To make sure the two cases are easy for Mary to use, we have to make them both as small and light as possible. Therefore, every electronic component should be located at its specific place. We did a lot of tests to decrease the size.

Prototyping Process Findings

Though we each worked on separate components of the ICD, the prototyping process helped teach us a variety of lessons not only about our individual components, but also about working on a physical computing project in general. From questions regarding how our overall project would be powered to what final form it would take, there were several moments throughout the prototyping process where we had to answer questions as a team rather than individually. This was somewhat surprising-even though we were all working on vastly different pieces of technology, the choices we each made on our parts had effects on the overall team project that were sometimes subtle and other times drastic. It was partly due to this that our final version was incomplete- we didn’t truly begin accommodating each other’s work until the final stages of the project. By that point, it was too late to make some much needed adjustments, such as making the ICD’s case less sharp and improving the ICD’s attachment to a wheelchair which were recommended to us from our critiques. We did not intentionally ignore this feedback, but unfortunately we ran out of time to address most of it.

Gantt Tracking

Our team’s Gantt chart- visualizing our originally planned project schedule

Overall, we were able to roughly follow our Gantt chart to about 75% accuracy. Divergences from our schedule were mainly due to longer than expected debugging times, slow-arriving components that were stuck in transit for longer than preferred, as well as other miscellaneous issues such as group members falling ill during some days.

Conclusions and Lessons Learned

Feedback

Some feedback that stood out to us were the following:

  • “Develop ergonomics, far reach in objectives file transferring”
  • “There are several steps involved in retrieving files; but, those could be incorporated into an end-of-day routine. The device is a bit large and the sharp corners could be uncomfortable.”
  • “They really should have considered how their client would use this and added some personality of their client into it.”
  • “A good idea. Make attachment to chair more stable.”

The reason why these pieces of feedback stood out to us was because they reinforced ongoing discussions we were already having regarding the direction of our project. Adding “personality” and making file transferring simpler, for instance, were goals we wanted to achieve but didn’t really figure out how to do so. This was partly due to delays in our planned schedule, and ultimately by the time we shifted our focus to the form of our project there was too little time to implement some of the recommended changes. That being said, all of the feedback received was insightful and extremely helpful for possible future iterations of our project.

Thoughts on Remote Work

In general, it was smooth to work remotely. At the first beginning, we lost contact with Mary but we got into a collaboration with her successfully soon. Sometimes we did different parts of the project individually but we still cared about the work from each other. Everyone put a lot of effort into this project and often continued working after class alone. If we had another chance to work together remotely, it might be a good way for better collaboration by updating the process once after work.

Takeaways from working with Mary

This journey looked like we were doing a device to help Mary, but literally it was Mary who inspired us a lot. She never gave up her love towards life though her mobility was limited. She had her own hobbies and work, her life was so adorable and she got a lot of sense of achievement during her tutorial work. “I really enjoy my life.” Mary said in her first interview with us. At that time we four were all touched by her strong positive vibe.

Concluding Thoughts

Even though our final prototype was somewhat distant from our original vision, the process we all went through while working on the prototype was very educational and insightful. We each learned different things about not just our components, but about the process of prototyping a physical computing project as well as working as a team in general.

Given a second go at the project knowing what we know now, our biggest changes would have to do with how we worked on the project in general. Our decision to split the ICD into four separate parts, one for each member, seemed like an obvious one at first but in retrospect proved to be a hindrance in the final stages of our prototyping process. Since we were all the resident “expert” on the part we were working on within the group, having a group member absent severely hampered our ability to move the project forward. This hurt us a lot after carnival, where some members of the group got sick and as a result had to miss some work sessions- negatively impacting the whole group’s progress.

A possible solution would be to assign two members to each component. This would greatly help with the aforementioned absence problem, and it would have also had positive effects on each component itself since there would be two minds tasked to making progress on each part instead of just one. Additionally, we would also resolve certain prototype issues, like choosing a battery and final form, as soon as possible instead of saving that for the very end given the importance of accessibility for our client.

Some other takeaways include establishing a debugging process for components early on, not underestimating the soldering process, properly gauging capabilities/knowledge early on in the process, and removing/combining features earlier! These takeaways were largely individual- Juan’s takeaway (the debugging process) was the result of too many hours spent trying to debug what ended up being broken components. Sumayya’s, on the other hand, centered on her experience tackling the complicated voice recording system. Instead of pivoting to a different direction earlier, her various attempts to get the component working backfired as other components that were behind in terms of progress would have made major use of an extra mind. Yongwen and Tristan also had their fair share of takeaways stemming from their assigned components. Overall, however, we can simply say that each one of us learned something valuable about our very own approaches to different problems.

Technical details

Diagrams

Remote Controller Diagram

 

Necklace Diagram

Code

// Thanks to ArduCAM Mini demo (C)2018 by Lee, found at http://www.ArduCAM.com
/*Image Capture Device - Remote/Camera Code,
Juan Meza, Tristan Hineman
This code operates the remote's various functionalities, including
how it takes user input, how it sends data wirelessly, and how it takes 
and saves images taken from the device. 
*/

//SPI Mosi-11, Miso-12, Sck-13

#include <nRF24L01.h>
#include <RF24.h>
#include <EEPROM.h>

#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include <SD.h>
#include "memorysaver.h"

#define BUTTON 8
#define BUTTON_BLUE 2
#define LED_BLUE 6 //blue LED
#define LED_RED 5 //red LED
#define SD_CS 9
#define FRAMES_NUM    0x00

const byte address[6] = "42069";
bool isButtonPressed = false;
unsigned long buttonTime;
int ButtonPresses = 0;

// set pin 7 as the slave select for the digital pot:
const int CS = 7;

RF24 radio(3, 4); // CE, CSN

//camera
//This demo can only work on OV5640_MINI_5MP_PLUS or OV5642_MINI_5MP_PLUS platform.
#if !(defined (OV2640_MINI_2MP_PLUS))
#error Please select the hardware platform and camera module in the ../libraries/ArduCAM/memorysaver.h file
#endif


bool is_header = false;
int total_time = 0;

#if defined (OV2640_MINI_2MP_PLUS)
ArduCAM myCAM( OV2640, CS );
#endif

uint8_t read_fifo_burst(ArduCAM myCAM);

void setup() {

  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(BUTTON_BLUE, INPUT_PULLUP);
  pinMode(LED_BLUE, OUTPUT);
  pinMode(LED_RED, OUTPUT);

  buttonTime = millis();

  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();

  // camera
  uint8_t vid, pid;
  uint8_t temp;
  
#if defined(__SAM3X8E__)
  Wire1.begin();
#else
  Wire.begin();
#endif

  Serial.begin(115200);
  
  Serial.println(F("ArduCAM Start!"));
  // set the CS as an output:
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);
  // initialize SPI:
  SPI.begin();
  //Reset the CPLD
  myCAM.write_reg(0x07, 0x80);
  delay(100);
  myCAM.write_reg(0x07, 0x00);
  delay(100);
   
   
  while (1) {
    //Check if the ArduCAM SPI bus is OK
    myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
    temp = myCAM.read_reg(ARDUCHIP_TEST1);
    if (temp != 0x55)
    {
      Serial.println(F("SPI interface Error!"));
      delay(1000); continue;
    } else {
      Serial.println(F("SPI interface OK.")); break;
    }
  }


#if defined (OV2640_MINI_2MP_PLUS)
  while (1) {
    //Check if the camera module type is OV2640
    myCAM.wrSensorReg8_8(0xff, 0x01);
    myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
    myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
    if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 ))) {
      Serial.println(F("ACK CMD Can't find OV2640 module!"));
      delay(1000); continue;
    }
    else {
      Serial.println(F("ACK CMD OV2640 detected.")); break;
    }
  }
  
#endif


  //Initialize SD Card
  while (!SD.begin(SD_CS))
  {
    Serial.println(F("SD Card Error!")); delay(1000);
  }
  Serial.println(F("SD Card detected."));
  //Change to JPEG capture mode and initialize the OV5640 module
  myCAM.set_format(JPEG);
  myCAM.InitCAM();
  myCAM.clear_fifo_flag();
  myCAM.write_reg(ARDUCHIP_FRAMES, FRAMES_NUM);
}


void loop() {

  myCAM.flush_fifo();
  myCAM.clear_fifo_flag();
#if defined (OV2640_MINI_2MP_PLUS)
  myCAM.OV2640_set_JPEG_size(OV2640_1600x1200);
#endif

  int buttonVal = digitalRead(BUTTON);
  //Serial.println(buttonVal);
  //as a reminder, when a button is being pressed buttonVal turns to zero

  int buttonBlueVal = digitalRead(BUTTON_BLUE);

  if (buttonVal == 0) {
    Serial.println("red is being pressed");
    digitalWrite(LED_RED, HIGH);

    myCAM.start_capture();
    Serial.println(F("start capture."));
    total_time = millis();
    while ( !myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));
    
    Serial.println(F("CAM Capture Done."));
    total_time = millis() - total_time;
    Serial.print(F("capture total_time used (in miliseconds):"));
    Serial.println(total_time, DEC);
    total_time = millis();
    
    read_fifo_burst(myCAM);
    total_time = millis() - total_time;
    Serial.print(F("save capture total_time used (in miliseconds):"));
    Serial.println(total_time, DEC);
    //Clear the capture done flag
    myCAM.clear_fifo_flag();
    digitalWrite(LED_RED, LOW); //turn off to signify that image has been taken
    
  }

  if (buttonBlueVal == 0) {
    Serial.println("blue is being pressed");
    const char text[] = "ACTIVATE NECKLACE";
    radio.write(&text, sizeof(text));
    digitalWrite(LED_BLUE, HIGH);
    delay(500); //do some blinking first
    digitalWrite(LED_BLUE, LOW);
  }

  if ((buttonVal == 0 || buttonBlueVal == 0) && millis() - buttonTime >= 500) { //button is being pressed for reasonable time

    //Serial.println(millis()-buttonTime);
    ButtonPresses++;
    buttonTime = millis(); //reset buttonTime for next button press
    //Serial.println("Button is being pressed.");
    Serial.println("Button has been pressed this many times:");
    Serial.println(ButtonPresses);

  }

  else {
    //Serial.println("Button is NOT being pressed.");
  }

  if (ButtonPresses >= 1) {
    //digitalWrite(LED1,HIGH); //on, LOW for off

  }

  if (ButtonPresses == 2) {
    //digitalWrite(LED2,HIGH);

  }

  if (ButtonPresses > 2) { //more than 2,  reset!
    Serial.println("TIME TO RESET");

    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_RED, LOW);

    delay(300); //do some blinking first

    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_RED, HIGH);

    delay(300);

    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_RED, LOW);

    ButtonPresses = 0; //reset total button presses
  }
}

uint8_t read_fifo_burst(ArduCAM myCAM)
{
  uint8_t temp = 0, temp_last = 0;
  uint32_t length = 0;
  static int i = 0;
  static int k;
  char str[16];
  File outFile;
  byte buf[256];
  length = myCAM.read_fifo_length();
  Serial.print(F("The fifo length is :"));
  Serial.println(length, DEC);
  
  if (length >= MAX_FIFO_SIZE) //8M
  {
    Serial.println("Over size.");
    return 0;
  }
  if (length == 0 ) //0 kb
  {
    Serial.println(F("Size is 0."));
    return 0;
  }
  
  myCAM.CS_LOW();
  myCAM.set_fifo_burst();//Set fifo burst mode
  i = 0;
  while ( length-- )
  {
    temp_last = temp;
    temp =  SPI.transfer(0x00);
    //Read JPEG data from FIFO
    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
    {
      buf[i++] = temp;  //save the last  0XD9
      //Write the remain bytes in the buffer
      myCAM.CS_HIGH();
      outFile.write(buf, i);
      //Close the file
      outFile.close();
      Serial.println(F("OK"));
      is_header = false;
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
      i = 0;
    }
    if (is_header == true)
    {
      //Write image data to buffer if not full
      if (i < 256)
        buf[i++] = temp;
      else
      {
        //Write 256 bytes image data to file
        myCAM.CS_HIGH();
        outFile.write(buf, 256);
        i = 0;
        buf[i++] = temp;
        myCAM.CS_LOW();
        myCAM.set_fifo_burst();
      }
    }
    
    else if ((temp == 0xD8) & (temp_last == 0xFF))
    {
      is_header = true;
      myCAM.CS_HIGH();
      //Create a avi file
      EEPROM.get(0, k);
      k = k + 1;
      EEPROM.update(0, k);
      
      Serial.print("Picture number=");
      Serial.println(k);
      itoa(k, str, 10);
      strcat(str, ".jpg");
      
      //Open the new file
      outFile = SD.open(str, O_WRITE | O_CREAT | O_TRUNC);
      
      if (! outFile)
      {
        Serial.println(F("File open failed"));
        while (1);
      }
      
      myCAM.CS_LOW();
      myCAM.set_fifo_burst();
      buf[i++] = temp_last;
      buf[i++] = temp;
    }
  }
  myCAM.CS_HIGH();
  return 1;
}

 

/*               
* Thanks to Dejan Nedelkovski, www.HowToMechatronics.com
/*Image Capture Device - "Necklace" Code,
Juan Meza 
This code operates our necklace's wireless capabilities.
It where the necklace should look for a signal and 
performs an action after finding said signal. 
*/


#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <string.h>

#define LED_TEST 3 //green temp LED

const byte address[6] = "42069";
RF24 radio(7, 8); // CE, CSN

void setup() {

  pinMode(LED_TEST,OUTPUT);
  
  Serial.begin(115200); 
  radio.begin(); 
  radio.openReadingPipe(0, address); 
  radio.setPALevel(RF24_PA_MIN); 
  radio.startListening(); 
  
  Serial.println("Receiver is ready to receive...");
  digitalWrite(LED_TEST,HIGH);
  delay(300); 
  digitalWrite(LED_TEST,LOW); 
  
}

void loop() {
  
  if (radio.available()) {
    char text[32] = "";
    radio.read(&text, sizeof(text));
    Serial.println(text);

    String key = "ACTIVATE NECKLACE";

    if (String(text) == key){
      //blink LED! 
      digitalWrite(LED_TEST,LOW); 
      digitalWrite(LED_TEST,LOW); 
      delay(300); 
      digitalWrite(LED_TEST,HIGH);
      digitalWrite(LED_TEST,HIGH);
      delay(300);
    }
  }
  
}

 

Special thanks to Professor Zacharias and everyone else who helped us get so far!

]]>
Team Callisto – Interview Documentations https://courses.ideate.cmu.edu/60-223/s2022/work/team-callisto-interview-documentations/ Mon, 28 Mar 2022 02:56:37 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15733 Introduction: 

Team Callisto had the opportunity to speak with Mary D’Ottavio to design an assistive device for Mary’s daily life. Through the Ideate: Physical Computing course at CMU we were able to meet with Mary and ask her about how she navigates her life with her physical disabilities. The Team consisting of Sumayya, Yongwen, Juan and Tristan held multiple interviews with Mary to really understand what type of device would benefit her. On Wednesday, March 23 Mary told the team about her love for sports, writing, card collecting and emphasized some issues regarding accessing items that are far out of reach. After some brainstorming, the team decided to meet with Mary again on March 27 to go over their ideas and designs for another brainstorming session. 

Agenda:

Interview 1:

  1. General introduction of the team and Mary
  2. Thank for Mary for taking the time to do the interview, and ask what encouraged to join the project
  3. Briefly explain project and establish the expectations we have
  4. Ask what she does in daily life (try to branch off of the reason she joined the project)
  5. Ask about hobbies
  6. Ask about difficulties she has in her daily life
  7. Ask about if she has any ideas for projects
  8. Brainstorm some potential solutions to problems that Mary may have mentioned from the last few questions
  9. Summarize the meeting
  10. Thank Mary for coming and let her know we will keep her updated about our ideas.

Interview 2:

  1. Short recap of last meeting, briefly discussing any key takeaways [5-10 Minutes]
  2. Discuss any topics/ideas Mary may have thought of since last meeting 
  3. Go over our current project ideas [5 minutes per idea, 20 minutes total] 
  4. Get feedback from Mary, ideally try to narrow down to 1-2 most appealing ideas
  5. Decide whether or not to move forward with one specific project, or to keep brainstorming

Summary and Takeaways:

Through the interviews we learned a lot about Mary’s activities and daily life. We were inspired by her strive to be independent and the many techniques she’s used to do her tasks on her own as much as possible. Although we were happy to learn Mary is very independent, this initially made it difficult to come with solutions that Mary would truly benefit from. It is why our first project idea was a solution to help with difficulties related to physical concerns such as reaching for items at the back of a closet or refrigerator. During the first interview we had discussed solutions to this problem but after a conversation with the professor, we realized our solution was very mechanical. And so, we went back to our notes from the first meeting and held a second interview with Mary. 

During the second interview, we clarified the purpose of the project was to find solutions that involve electronics. We eventually learned that Mary loves to write whenever she gets the chance. We wondered if Mary has difficulty noting ideas that she may randomly think of. As a result we suggested a device that could record notes by the press of a button. This way, Mary doesn’t have to worry about the time and difficulty of typing ideas. Instead, she simply scrolls through her recordings, minimizing hand movement and maximizing her ability to write more stories. 

Images of Mary’s fridge from the first Interview. We wanted to see the layout of her fridge when we were discussing the accessibility problem with items at the far back.

 

 

 

 

 

 

 

 

 

 

Design inspired from our second interview. This device is a recorder that would help Mary note ideas whenever and wherever.

 

Thoughts:

The two meetings we had with Mary both went well. Mary is a very positive lady who loves her life so much, which inspires us not only for this assignment but also for our life attitude. We followed the agenda to carry on the meeting and Mary cooperated with us nicely. Unfortunately we could not meet in person for the second interview. But even over the phone, we had a productive discussion about the pros and cons of different project ideas. We appreciated Mary’s honesty and feedback on our ideas as it greatly helped us revise our designs. Since everything went so well, we ended the meeting about 10 minutes earlier than expected. The second meeting was truly productive which made us know better about Mary’s needs and helped us decide on the final design: the voice recorder device. All the team members are very excited about this idea and hope the project goes well and benefits Mary in the future.

]]>
Water Drinking Recorder https://courses.ideate.cmu.edu/60-223/s2022/work/15228-2/ Fri, 11 Mar 2022 00:38:31 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15228 Device Function: The core of this device is a scale that weighs the amount of water in a bottle and updates how much of your goal you have reached based on the difference in measurements. 

Moving Image: 

 

Scale Image:

Detail Image: 

 

This is the LED strip that is a visual indicator of when the user reaches their goal

 

One of my focuses for this device was to make it visually appealing. I did this by creating a separate space for each component of the device. Arduino at the bottom, breadboard in the middle and load cell at the top.

Function Image:

LCD display asks to measure water. If user answers yes, next message is displayed.

After answering yes on the serial monitor, the LCD display shows how much water the user had and how much they have left to reach their goal.

Once the goal is met, the LED strip turns on and the device asks if the user wants to restart.

 

Progress:

Decision Point 1:  When I first designed my device, I had thought that my plates only needed to be half an inch wider in radius than my personal water bottle. I laser cut three plates according to my intial sketches but soon realized that these plates were too small. Although they could still hold my water bottle, I realized there wasn’t enough space for all my electronics and that when the bottle was placed on the device, it wasn’t pleasing to look at. To fix the issue I cut larger sized plates and printed 4 plates instead of 3 plates. I now had enough space for the Arduino and breadboard and was satisfied with the overall aesthetic of my device with this change.

This shows a comparison between the original smaller plates and the larger plates cut after revision.

Decision Point 2: I had mainly worked on my device using a temporary 1kg load cell while I waited for a 5kg load cell to arrive. It turns out that the measurements of the 1kg load cell were not the same as the measurements on the 5kg load cells. But given that there was not enough time to re-laser cut the plates that would hold the load cell, I decided to move forward using double sided sticky tape. Unfortunately I could not demo my device using my personal 40 oz water bottle as the double sided sticky could not keep the plate sturdy with heavy objects. Fortunately, with a standard sized plastic water bottle, my device still worked as intended.

Spacer holes don’t match with the holes on the 5kg load cell

Chose to use double sided sticky tape to attach load cell due to time crunch

 

 

 

 

 

 

 

 

 

 

 

Design Process:

First Design on TinkerCad
The bottom plate was supposed to be thicker to attach LCD and LED strip. Everything including the support bars were to be 3D printed.

 

Second Revision
Progress after changing the plate sizes. At this stage I was testing with the 1kg load cell. There was no led strip or LCD display.

 

 

 

 

 

Final Revision (before load cell change)
I added an LED strip, the LCD Display and secured all electronics to their respective plates. I also started testing using an actual water bottle.

Discussion: 

In Class Crit:

I agree that my device would be “convenient and easy to use for college students”. In fact my inspiration for this project came from the fact that I tend to forget to drink water as I do my coursework. And so having something portable and on my desk makes it easier to remember. Many comments pointed out that the device looked unstable. One person suggested to “add more pole supports to make the device more stable since it looked like it would tip if the bottle wasn’t put right in the middle”. Along with this suggestion I would also use a better supported load cell. As I mentioned above the use of double sided sticky tape was a large reason why the device was unstable. Adding extra poles and bolts would definitely help. Overall I’m glad many people liked the use of plates to make the device look cleaner and aesthetic as that was a large goal of mine. 

Self Critique:

     I am happy with my project but I’m not completely satisfied with it. I think that despite my negative thoughts that I will not be able to complete this project, I still met my main goals. For that I am very proud of myself. Originally my idea was for my device to take all measurements by itself and have a more attractive LCD display. The current version of my project is more user dependent and heavily relies on the user to take measurements and function as intended. Going forward I would like to remove this reliability or at least integrate it within the device rather than an external computer. 

Lessons:  

    I learned that I could apply my programming skills to actual products and not just assignments in my classes. I found the coding for this project especially difficult because the load cell takes continuous measurement. I had to be in control of which of the stream of readings from the load cell to use for calculations. I worked around this issue by adding a user interface through the Serial Monitor. I asked the user when they wanted to take a measurement and only when “yes” was entered did my code move forward with the program. 

    I also learned that all is not lost if things don’t go as planned at the last minute. I was very afraid when I received my demo load cell the day before the assignment was due and found that my laser cut pieces don’t align with the load cell holes. Instead of using screws, I went with double sided tape and found that my device still worked. In the future though, I want to take this as a lesson to have my parts ahead of time. In general I learned that its important to set milestones and work with what you have each step of the way instead of waiting to perfect everything. This way I’m not crunching for time and stress less. 

Next Steps:

     I would like to make another iteration if I have the time and opportunity to do so. Given that I do have both, I would like to remove the user input from my laptop and instead add buttons to the device for user input. I would also change the LCD display so that it shows a visual icon (like a battery) to indicate how much of my goal I have met. I would also laser print the plates again so that I can bolt the load cell rather than use tape. 

 

Technical:

Schematic:

Block Diagram:

Code:

 

#include <Q2HX711.h>
#include <LiquidCrystal_I2C.h>
#include <PololuLedStrip.h>
#include "HX711.h"


/*
 * @Title: Water Drinking Recorder
 * @author: Sumayya Syeda
 * @Description: This program uses the Serial Monitor to allow for user input and take measurements of items placed on the load cell. 
                 Based on the difference between consecuitive measurements, the program updates properties that indicates how much water is left, 
                 how much water is currently in the bottle and if the user goal has been met. 
                 When the goal is met the LEDs turn on. 
 
   HX711 Calibration code by ElectroPeak
   https://create.arduino.cc/projecthub/electropeak/digital-force-gauge-weight-scale-w-loadcell-arduino-7a7fd5
*/


HX711 scale;
PololuLedStrip<12> ledStrip;
LiquidCrystal_I2C screen(0x27, 16, 2);

const int LOADCELL_DOUT_PIN = 4;
const int LOADCELL_SCK_PIN = 5;

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

float goal, og_water, curr_water, water_drank;
float old_curr, old_drank;
float units;
bool read_init, no_change, reached_goal, no;
const float CALIBRATION_FACTOR = 505;

void setup() {
  goal = 0;
  og_water = 0;
  curr_water = 0;
  water_drank = 0;
  old_curr = 0;
  old_drank = 0;
  units = 0;

  Serial.begin(9600);

  screen.init();
  screen.backlight();
  screen.home();
  screen_print(screen, "Track your drinking!", 0, 0);

  turnLEDOff();

  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  calibrate_setup();
  goal_setup();


}
void loop() {
  Serial.print("---- ");
  Serial.print();
  Serial.println(" ----");

  //tare;
  if (Serial.available()) {
    char temp = Serial.read();
    if (temp == 't' || temp == 'T') {
      scale.tare();  //Reset the scale to zero
    }
  }

  if (!reached_goal) {
    screen.clear();
    Serial.println("Measure water?"); //Prompt User for Input'
    screen_print(screen, "Measure water?", 0, 0);

    Serial.print("OG:");
    Serial.println(og_water);


    while (Serial.available() == 0 && !reached_goal) {
      print_measure_message();
    }

    String response = Serial.readString();

    if(response == "yes") {
      read_scale();
      curr_water = units;

      float diff = old_curr - curr_water;
      if (abs(diff) < 1) {
        diff = 0;
      }

      Serial.print("OG: ");
      Serial.println(og_water);
      screen.clear();
      screen_print(screen, "OG:", 0, 0);
      screen.setCursor(4, 0);
      screen.print(og_water);

      Serial.print("curr water: ");
      Serial.println(curr_water);
      screen_print(screen, "curr:", 10, 0);
      screen.setCursor(15, 0);
      screen.print(curr_water);

      //no change state
      if (curr_water <= (old_curr + .5) && curr_water >= (old_curr - .5)) {
        print_no_change_message();
        delay(2000); //Display message for 2 seconds
        screen.clear();
      } 
      else { //change states
        
        if (diff > 0) {      //water decreased in bottle
          water_drank += diff;
          print_water_drank_message();
          delay(3000); //Display message for 3 seconds
          screen.clear();
        } 
        else if (diff < 0) {    //water increased decreased in bottle
          og_water += diff * -1;
          print_water_added_message(diff);
          delay(3000); //Display message for 3 seconds
          screen.clear();
        }
      }
    }
    else if (response == "no") { //did not want to measure water
      screen.clear();
      Serial.print("OG: ");
      Serial.println(og_water);
      screen_print(screen, "OG: ", 0, 0);
      screen.setCursor(3, 0);
      screen.print(og_water);

      screen_print(screen, "You drank: ", 0, 1);
      screen.setCursor(10, 1);
      screen.print(water_drank);
      screen_print(screen, "Left: ", 0, 2);
      screen.setCursor(10, 2);
      screen.print(goal - water_drank);

      delay(4000); //wait 4 seconds before asking again
    }
  }

  //Reached goal state
  if (water_drank >= (goal - 0.6)) {
    Serial.println("You reached your goal!!");
    goal = 0;
    turnLEDColors__on();
    reached_goal = true;
    if (!no) {
      Serial.println("Again?");
      screen_print(screen, "Again? ", 0, 0);
      while (Serial.available() == 0) {
        // Wait for User to Input Data
      }
      if (Serial.readString() == "yes") {
        turnLEDOff();
        goal_setup();
      }
      else if (Serial.readString() == "no") { //does not want to measure again
        no = true;
        reached_goal = true;

      }
    }
  }

  if (no) { //if don't want to measure again, blink LEDs
    screen.clear();
    turnLEDOff();
    delay(20);
    turnLEDColors_on();

  }

  old_curr = curr_water;
  old_drank = water_drank;

}

/**Calibrate the load cell. Only needs to be done once**/
void calibrate_setup() {
  scale.set_scale(CALIBRATION_FACTOR); //Adjust to this calibration factor
  scale.tare();  //Reset the scale to 0

  long zero_factor = scale.read_average(); //Get a baseline reading
}

/**Reads the load scale measurement and sets the global variable: "units". "Units" can be accessed anywhere to get the weight measurment in ml**/
void read_scale() {
  units = scale.get_units(), 5;
  if (units < 0)
  {
    units = 0.00;
  }
}

/**General print function for lcd display**/
void screen_print(LiquidCrystal_I2C lcd, String message, int x, int y) {
  lcd.setCursor(x, y);
  lcd.print(message);
}

/**Turn on LEDs off**/
void turnLEDOff() {
  for (uint16_t i = 0; i < LED_COUNT; i++)
  {
    colors[i] = off;
  }
  ledStrip.write(colors, LED_COUNT);
}

/**LED changes color on a gradient**/
void turnLEDColors_on() {
  byte time = millis() >> 2;
  for (uint16_t i = 0; i < LED_COUNT; i++)
  {
    byte x = time - 8 * i;
    colors[i] = rgb_color(x, (255 - x), x);
    delay(10);
  }

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

}

/**sets up goal variable by asking user**/
void goal_setup() {

  /**Ask for goal and store value in "goal"**/
  Serial.println("What is your goal?");
  screen.clear();
  screen_print(screen, "What is your goal?", 0, 0);
  while (Serial.available() == 0) {
    // Wait for User to Input Data
  }
  goal = Serial.parseInt();
  Serial.println("Place your water bottle on the scale!");
  screen_print(screen, "Place your bottle on the scale!", 0, 0);

  delay(3000); //give time to place bottle

  read_scale();
  screen.clear();

  if (read_init == false) {
    /**set initial water readings**/
    og_water = units;
    curr_water = units;
    old_curr = units;
    Serial.print("Init water: ");
    Serial.println(og_water);
    screen_print(screen, "OG water: ", 0, 0);
    screen.setCursor(0, 10);
    screen.print(og_water);
    read_init = true;  //once a reading is taken, don't take another
  }

  /**print weight to serial monitor and LCD**/
  Serial.print("Weight: ");
  Serial.print(units);
  Serial.print(" grams");
  Serial.println();

  screen.clear();
  screen_print(screen, "Weight(ml): ", 0, 0);
  screen.setCursor(10, 0);
  screen.print(units);
  delay(1000);

  reached_goal = false;
  water_drank = 0;
  old_drank = 0;

}

/**Message to print while device waits for user to measure their water**/
void print_measure_message() {
  screen_print(screen, "Measure water?", 0, 0);
  delay(1000);
  screen.clear();
  screen_print(screen, "OG:", 0, 0);
  screen.setCursor(5, 0);
  screen.print(og_water);

  screen_print(screen, "You drank:", 0, 1);
  screen.setCursor(11, 1);
  screen.print(water_drank);
  screen_print(screen, "Left:", 0, 2);
  screen.setCursor(10, 2);
  screen.print(curr_water);
  delay(1000);
  screen.clear();
}

/**Message to print if there is no change between measurements**/
void print_no_change_message() {
  Serial.println("No change!");
  Serial.print("You drank: ");
  Serial.println(water_drank);

  screen_print(screen, "No change!", 0, 1);
  screen_print(screen, "You drank:", 0, 2);
  screen.setCursor(10, 2);
  screen.print(water_drank);
}

/**Message to print if the user drank some water**/
void print_water_drank_message() {
  Serial.print("You drank: ");
  Serial.println(water_drank);
  Serial.print("You have left: ");
  Serial.println(curr_water);

  screen_print(screen, "You drank: ", 0, 1);
  screen.setCursor(10, 1);
  screen.print(water_drank);
  screen_print(screen, "Left: ", 0, 2);
  screen.setCursor(10, 2);
  screen.print(goal - water_drank);

}

/** Messsage to print if the user added some water to bottle**/
void print_water_added_message(float diff) {
  Serial.print("Water added: ");
  Serial.println(diff);
  Serial.print("You now have: ");
  Serial.println(og_water);

  screen_print(screen, "Add: ", 0, 1);
  screen.setCursor(10, 1);
  screen.print(diff * -1);
  screen_print(screen, "Left: ", 0, 2);
  screen.setCursor(10, 2);
  screen.print(goal - water_drank);
}

 

 

]]>