Purpose: An attachable light to a water bottle that reminds you to drink water
Video of the water bottle initially starting in green state, for the video purpose I changed the time limit to alert you to drink water from 15min to 10 seconds. Then the ring led blinks yellow until a user picks it up and then a blue animation starts until a user sets the bottle back down, then the color changes back to green because you’ve taken a drink of water.
***near the end of the video I said “if you go longer than 10 seconds without drinking water red light flashes”, I meant longer than 20 seconds (twice the amount of time for yellow light) before the red light flashes***
Process Images and Review
Throughout the process, one significant choice I made was to use a neo pixel ring instead of soldering a bunch of LEDs in a circle to a protoboard and then using a saw to cut the corners of the protoboard. I got this idea from looking at all the parts in my kit, and seeing a circle ring of LEDs which wasn’t a part in the Arduino kit, but something I could probably learn more about and test it in a reasonable amount of time. This decision definitely saved me a lot of pain and time, and allowed me to learn how to use a neo pixel which was easier than it sounds. To attach it to the belt I initially bent paper clips to make two different slots, painted them black, and hot glued it to the back of the ring. Since the paper clips I was using are made of metal it caused some noise disturbance and potentially unwanted connections which caused to me to end up attaching the back of the ring to the belt with cut up Command Strips which also worked.
Another significant choice I made was to hard code all the water capacity angles into my code. This meant I had to gather some data on how far I would have to tilt my water bottle to get it at 90% capacity, 80% capacity, 70% capacity, etc within the dimensions of my water bottle. I found that the bounds between each of the water capacities (especially around 50%-60%) were very similar and often overlapped, which was a bit of a surprise but something I could manage in my code. It took around an hour to collect this data and two minutes to “hard code” so it wasn’t too bad.
The last significant choice I made was to add a blue light animation that would begin once the water bottle was picked up and end once the bottle was back on a flat surface. I speak more about where this decision came from and the ultimate outcome of this idea in my discussion section of documentation.
Video of blue light animation
Video of water bottle moving with blue light animation playing
Discussion
Before the project was finalized, I had the opportunity to come into class and show my prototype to my classmates to get some constructive feedback on the model. Feedback from classmate (A) was “you know what you could add? An animation with the neo pixel ring that starts as your lifting the bottle and drinking out of it.” Classmate (B) stated “you could de-solder the accelerometer and have it flush with water bottle to make the design cleaner.” I was motivated by both responses and added a function to my code which begins an animation with blue lights once a user picks up the bottle and ends when a bottle is put down on a flat surface. I attempted implementing the second response, but I found it extremely hard to de-solder the accelerometer while taking out pins because the solder cooled as soon as I melted it and there wasn’t much free room between the pins to move my soldering iron around which ultimately resulted in me abandoning this idea.
Abandoning the latter idea wasn’t all too bad, I just hid the “bulky” accelerometer by placing it to the opposite of the neo pixel ring on the belt strap. Overall, this managed to do the trick but one aspect of the project I was still unsatisfied with was how visible the wires were in my project. I couldn’t think of a way around this because the object was made to move. In another iteration of the project I probably would’ve taken the time to learn more about the mini-Arduino since my electrical schematic only involved the Accelerometer, and neo pixel ring (3 analog pins, 1 digital pin, power, and ground). The slimness of the mini-Arduino would’ve allowed me to store the wires in a nice box that I could attach onto the belt strap.
Aside from the visible wires I’m happy that I got to learn how to use a neo pixel ring, how to write functions in Arduino, and got to a semi-functioning project. It’s buggy due to my code, I found that coding if the water bottle was picked up and the angle was constantly changing was harder than I initially thought. In my code, I implemented a function to sense if the angle was changing and it always evaluated to true even when the bottle was on a flat surface for some time. I used this function to control the blue light animation in order to demonstrate that the user was drinking water, and since the function always evaluated to true, the animation was always playing. This was definitely not intentional and I got hung up on trying to fix it with no avail. On the next iteration, finding out how to fix this would probably be the priority because the animation was one feature I really liked about the project and would like to keep it for the future.
Technical information
/* Title: HYDRATION REMINDER * Owner: Amelia Lopez * 60223, project 2 * * Description: Code that alerts a user when to drink water * through the color of a neo pixel (inported library on line #) * green corresponds to a healthy level of water intake every 15 min * yellow reminds a user to drink water based on water capacity for the past 15 min * red alerts a user they haven't been drinking water for more than 30 minutes. * Water capacity is measured through the accelerometer which measures angle. * The steeper the angle, the less water is in the water bottle. * * Borrowed heavily from: * https://how2electronics.com/arduino-tilt-angle-distance-meter/ * in order to find angle from accelerometer * Pin mapping: Arduino pin | type | description ------------|--------|------------- A1 input X-pin Accelerometer A2 input Y-pin Accelerometer A3 input Z-pin Accelerometer ~11 output neo pixel ring (LED) (digital PWM~) */ #include <PololuLedStrip.h> // Create an ledStrip object and specify the pin it will use. PololuLedStrip<11> ledStrip; // Create a buffer for holding the colors (3 bytes per color). #define LED_COUNT 12 rgb_color colors[LED_COUNT]; //YELLOW= 25, 25, 0 //GREEN= 0, 20, 0 //RED = 35, 0, 0 //blue = 0, 14, 34 //DECLARING ARDUINO PINS const int ACCE_X = A1; const int ACCE_Y = A2; const int ACCE_Z = A3; const int RED_PIN = 11; const int GREEN_PIN = 10; const int BLUE_PIN = 9; //declaring variables useful for angle measurement #define ADC_ref 5 // ADC reference Voltage #define zero_x 1.799 #define zero_y 1.799 #define zero_z 1.799 #define echoPin 8 #define trigPin 9 #define selectSwitch 1 #define sensitivity_x 0.4 #define sensitivity_y 0.4 #define sensitivity_z 0.4 unsigned int value_x; unsigned int value_y; unsigned int value_z; float xv; float yv; float zv; float currAngle; float angleY; float angleZ; float min_angle; String colorName; //DECLARING INTS FOR LCD WAITING TIMES const int WAIT_TIME = 1000; unsigned long timeVariable = 0; //DECLARING INTS FOR WATER LEVEL const int WATER_WAIT_TIME = 15000; const int TOLERANCE = 2; const int angleFlat = 230; int water_capacity = 100; // unit: % int prev_capacity = 100; //unit: % unsigned long waterTimeVariable = 0; unsigned long yellowTimer = 0; float lastMinAngle; bool isDrinkining = false; bool yellowLightState = LOW; void setup() { analogReference(ADC_ref); //INPUT PINS pinMode(ACCE_X, INPUT); pinMode(ACCE_Y, INPUT); pinMode(ACCE_Z, INPUT); } void loop() { setGreen(); //set color to yellow to remind user to drink if (millis() - waterTimeVariable > WATER_WAIT_TIME) { //SET COLOR TO BLINKING YELLOW UNTIL ANGLE CHANGES prev_capacity = calculateCapacity(prev_capacity, calculateAngle()); currAngle = calculateAngle(); while (angleFlat - TOLERANCE <= currAngle)//blink yellow; reminder to drink { setYellow(); yellowTimer = millis(); yellowLightState == HIGH; currAngle = calculateAngle(); delay(500); setOff(); delay(500); if (millis() - yellowTimer >= 5000 and yellowLightState == HIGH) { yellowLightState = LOW; setOff(); yellowTimer = millis(); } currAngle = calculateAngle(); if (millis() - yellowTimer >= 5000 and yellowLightState == LOW) { yellowLightState = HIGH; setYellow(); yellowTimer = millis(); } currAngle = calculateAngle(); if (millis() - waterTimeVariable > 2 * WATER_WAIT_TIME) //set LED red; missed a drinking cycle { setRed(); } } while (angleIsChanging()) { blueAnimation(); water_capacity = calculateCapacity(prev_capacity, calculateAngle()); angleIsChanging(); } setGreen(); //check that the new capacity is lower than the previous. If not, light continues to blink yellow while (water_capacity == prev_capacity) { setYellow(); yellowTimer = millis(); yellowLightState == HIGH; currAngle = calculateAngle(); if (millis() - yellowTimer >= 500 and yellowLightState == HIGH) { yellowLightState = LOW; setOff(); yellowTimer = millis(); } water_capacity = calculateCapacity(prev_capacity, calculateAngle()); if (millis() - yellowTimer >= 500 and yellowLightState == LOW) { yellowLightState = HIGH; setYellow(); yellowTimer = millis(); } water_capacity = calculateCapacity(prev_capacity, calculateAngle()); } waterTimeVariable = millis(); } //taken a sip setGreen(); //while angle is changing, display the blue animation while (angleIsChanging()) { blueAnimation(); water_capacity = calculateCapacity(prev_capacity, calculateAngle()); angleIsChanging(); } timeVariable = millis(); } int calculateAngle() { //Taking in Accelerometer readings and converting it into an angle value_x = analogRead(ACCE_X); value_y = analogRead(ACCE_Y); value_z = analogRead(ACCE_Z); xv = (value_x / 1024.0 * ADC_ref - zero_x) / sensitivity_x; yv = (value_y / 1024.0 * ADC_ref - zero_y) / sensitivity_y; zv = (value_z / 1024.0 * ADC_ref - zero_z) / sensitivity_z; currAngle = atan2(-yv, -zv) * 57.2957795 + 180; //x angle angleY = atan2(-xv, -zv) * 57.2957795 + 180; //y angle angleZ = atan2(-yv, -xv) * 57.2957795 + 180; //z angle return currAngle; } int calculateCapacity(int prev_capacity, float min_angle) { if (247 < min_angle) return 100; if (225 < min_angle) return 90; if (208 < min_angle) return 80; if (204 < min_angle) return 70; if (203 < min_angle) return 60; if (200 < min_angle && prev_capacity == 60) return 50; if (200 < min_angle) return 40; if (198 < min_angle) return 30; if (196 < min_angle) return 20; if (193 < min_angle) return 10; return 0; } //set all pixels to yellow void setYellow() { for (uint16_t i = 0; i < LED_COUNT; i++) { colors[i] = rgb_color(25, 25, 0); //yel } ledStrip.write(colors, LED_COUNT); } //set all pixels off void setOff() { for (uint16_t i = 0; i < LED_COUNT; i++) { colors[i] = rgb_color(0, 0, 0); //yel } ledStrip.write(colors, LED_COUNT); } //set all pixels to red void setRed() { for (uint16_t i = 0; i < LED_COUNT; i++) { colors[i] = rgb_color(35, 0, 0); } ledStrip.write(colors, LED_COUNT); } //set all pixels to green void setGreen() { for (uint16_t i = 0; i < LED_COUNT; i++) { colors[i] = rgb_color(0, 20, 0); } ledStrip.write(colors, LED_COUNT); } //cascade of blue lights turning on void animationUp() { const int startingPoint = 4; for (int i = 0; i < (LED_COUNT / 2) + 1; i++) { colors[(startingPoint - i) % LED_COUNT] = rgb_color(0, 14, 34); colors[startingPoint + i] = rgb_color(0, 14, 34); if ((startingPoint - i) % LED_COUNT == -1) { colors[LED_COUNT - 1] = rgb_color(0, 14, 34); } ledStrip.write(colors, LED_COUNT); delay(150); } } //cascade of blue lights turning off void animationDown() { const int startingPoint = 4; for (int i = LED_COUNT / 2; i >= 0; i--) { colors[(startingPoint - i) % LED_COUNT] = rgb_color(0, 0, 0); colors[startingPoint + i] = rgb_color(0, 0, 0); if ((startingPoint - i) % LED_COUNT == -1) { colors[LED_COUNT - 1] = rgb_color(0, 0, 0); } ledStrip.write(colors, LED_COUNT); delay(150); } } //determine if angle is changing bool angleIsChanging() { return angleFlat - TOLERANCE > calculateAngle(); } //starts the blue animation void blueAnimation() { animationUp(); delay(100); //maybe 50ms instead animationDown(); delay(100); } //blinks yellow light void blinkYellow(){ setYellow(); delay(200); setOff(); delay(200); }