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/
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.
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.
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.
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.
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.
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.
Some feedback that stood out to us were the following:
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.
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.
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.
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.
Remote Controller Diagram
Necklace Diagram
// 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 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:
Interview 2:
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.
]]>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); }
]]>