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);
}