Note: The feature image is not ideal and I would have liked to take a picture of both the stop snac-ing box and the habit helper box.

Overview

Introduction

stop snac-ing is a box that would lock at all times and would open at certain times for a fixed duration to limit the intake of caffeinated chocolates; habit helper is a physical data visualization of the intake of caffeine over a week.

Over the last few years of college, I have been very dependent on caffeine where I would get headaches and become highly unproductive when I do not get my daily caffeine fix. Having caffeine was not a big problem until it affected my health and increased my heart rate a little too much.

Given that I am in my last semester of college, I wanted to reduce my caffeine dependency. My main form of caffeine intakes are ice soy lattes and caffeinated chocolate. I could usually control how many ice lattes I consume, but have had troubles limiting how many caffeinated chocolate I eat. (mainly because the caffeine doesn’t kick in as quickly)

Final Images

Note: I do not have detail photos for habit helper and images of users using both stop snac-ing and habit helper.

habit helper box

habit helper box (overall photo)

stop snac-ing box

closed stop snac-ing box (overall photo)

stop snac-ing box

opened stop snac-ing box (detail photo)

stop snac-ing box

labelled stop snac-ing box (detail photo)

stop snac-ing box disassembly structure

Final Video

Note: I was not able to take a full video of the different functionalities of both products before spring break and this is the closest I can get to a video.

Process images and review

Incorporating habit helper idea

One of the problems I faced when working on stop snac-ing is the ability to use the load cell.

load cell setup

When the box is unlocked, it is possible for users (me) to be tempted to take more than one chocolate bar each time. In order to keep track of how many chocolate bar each users take, and prevent user from taking more than one chocolate bar, I decided to use a load cell to weight the chocolates and the box will react when users take more than one chocolate bar.

I was only able to obtain a 300g load cell the week before the deadline and was able to do some initial testing of the product before incorporating it into the actual box.

broken load cell

However, in the process of incorporating it into the actual box, one of the wires connected to the load cell broke since it was really thin and fragile. Attaching the wires were really hard since it broke within the glue area, which was why I decided to order an extra load cell, but 750g this time, in the hopes that the wires are stronger. Since it was cutting close to the deadline and the load cell was a key part of the project, I decided to incorporate another idea I had to this project.

habit helper ideation

I decided to incorporate habit helper, a physical visualization of one’s habit over a year. The habit helper consists of three different section. The first section consists of seven LEDs, each representing a day of the week. The second section consists of five LEDS, each representing each week of the month. The third section consists of twelve LEDs, each representing a month in the year.

habit helper – initial breadboard

When laying out the LEDs on the breadboard, I came across the problem where it would be very difficult to have the LEDs be of a height higher than all the components.

This is why I decided to not use breadboards and start using protoboards instead.

protoboard layout – front

protoboard layout – back

In the process of writing the code for the habit helper, I found that the logic was slightly complicated, like whether a weekly light should be turned on and how to deal with months with different number of weeks.

I decided to spend more time on the creating the physical box instead of spending more time on the software since half the assignment is on the aesthetic of the boxes. This was why I decided to scale down habit helper by only keeping track of habits for a week rather than for an entire year.

Changing locking mechanism

My initial locking mechanism consists of two servos on the lid of the box and two slit faced cuboid in the front section of the body of the box.

initial prototyped locking mechanism

However, upon prototyping and testing it out on wood, I realize that it was not very secure. I decided to change the locking mechanism by having two ledges on the left and right of the body of the box. The two servos will remain on the lid of the box and would hook onto the ledge when locked.

Note: this is not an ideal image to show the locking mechanism. I was hoping to be able to get a view of the lock with the front “debugging” panel open.

stop snac-ing: final locking mechanism

This locking mechanism is much stronger than the initial design, but is still not perfect. This was when I decided to add a magnetic switch to the lid of the box to check whether the box is opened or closed. When the box is ever opened when it is not supposed to, the buzzer would then buzz until the box is closed.

Process Images for habit helper

The video below shows the demo of the software for habit helper. In the video, the weeks do not reset.

This next video is the updated software code, where the weeks would reset

The next video shows the incorporation of an LCD and setting a threshold for the daily caffeine limit

laser cutting habit helper box

Process Image for stop snac-ing

stop snac-ing box with magnetic switch

ensuring that the screw is flush to the wood piece

load cell cross section view

assembling the box

Discussion

Feedback

One suggestion I had was “a dispenser system” which “can better control the daily intake”. I think this is a really good idea and would be mechanically a fun challenge to take on. However, I feel that it would encourage me to constantly get my caffeine fix even if I don’t need it. In the case of the box, I want to be able to have a window to have caffeine if I need it, but since the box is always shut, it would tempt me less with the caffeinated chocolates. In the future, I want to be able to create a smarter way to control caffeine chocolate intake by making the duration to the next box unlock slightly longer if a bar of chocolate is taken.

Another critic I had was the problem when a user “forget(s) yo press the button or lie that (they) didn’t”. This is definitely a problem that would arise where users would fake the data. I guess self discipline is needed by the users who use the product. After all, the motivation to cut down caffeine has to be there in the user in the first place and devices can only be there to assist in them achieving their goals.

One verbal comment I got that I agreed with is the devices being the stick of the carrot and stick approach. Instead of rewarding users for not drinking caffeine, the LED lights up to “haunt” the users for drinking caffeine. It would be better for for habit helper to light up at the end of the day when the user drinks less caffeine than the set amount.

Reflections

Overall, I am pretty happy with how the project turned out. I think it was really nice that the second load cell got delivered before the deadline so I could incorporate them into the project.

One goal I had as I was working on my homework and project one was dealing with many wires and figuring out a way to minimize jumper wires.

In dealing with wires, I had two main rules:

  • Connections within the breadboard will solely consist of the single core wires just long enough to reach the connection and flush with the breadboard.
  • Jumper wires will only be used to connect the breadboard or any parts to the Arduino

In terms of the color of wires I used, I came up with a color system for better organization:

  • Red wires are used only as the main source of power (from the arduino)
  • Orange wires are used to power my parts and will go from thee arduino to the parts. (This color was chosen because it is closest to red)
  • Black wires are used only as the actual ground (to the ground of the arduino)
  • Grey wires are used to ground any parts
  • Yellow wires are used for output pins (like LEDs)
  • Blue wires are used for input pins
  • Green wires are used to connect to SDA
  • White wires are used to connect to SLC

With this system in place, I felt that I was better able to achieve my goal of organizing my wires. This also made it easier to debug when I have a problem.

Using cardboard to prototype took way more time than I expected. This was because as I was creating my prototype, I was not very familiar with laser cutting tools and felt that I would have to use cardboard as my final product. This was also why I actively tried to make the box as seamless as possible where the corrugated edge of the cardboard would not be seen from the outside. Since this requires more precision, it increased the amount of time needed to complete.

I was really surprised how much time it took to complete the project and found myself going through many iterations just to get simple mechanisms like the locking of the box working. With the use of laser cutter, my iterations could have been so much faster as compared to if I were to cut pieces of cardboard out.

Next Steps

If i were to build another iteration, I would work on the hinge of the boxes. I was not able to find a hinge that would allow my box to be opened and closed in a way where there isn’t a gap, which was why i decided that using duct tape was my best bet. Another thing I would work on would bee to reduce the amount of exposed cross section of the wood pieces and have the edges be more seamless.

testing of hinge

In terms of the software code, I would enable more customization from the box itself and not from the code so that users would be able to change their preferences without having to mess with the code.

Technical Information

Schematics

habit helper schematics

Code
/*
 * Project 2: stop snac-ing habit helper
 * Zoe Teoh
 * 
 * Description:  
 * 
 * stop snac-ing is a box that would lock at all times and would 
 * open at certain times for a fixed duration to limit the intake 
 * of caffeinated chocolates; habit helper is a physical data 
 * visualization of the intake of caffeine over a week.
 * 
 * 
 * Pin mapping:
 * 
 * pin   | mode   | description
 * ------|--------|------------
 * 5       output   servo motor
 * 7       input    magnetic switch
 * 12      output   buzzer
 * 
 */

// Servo Motor
#include <Servo.h>
Servo steeringMotor;

// Real Time Clock
#include <RTClib.h>
#include <Wire.h>
RTC_DS3231 rtc;
char t[32];
DateTime datetime;

// LCD
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 20, 4);

// Load cell
#include "HX711.h"
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;
HX711 scale;

// pin mapping
const int MAGNETPIN = 7;
const int MOTORPIN = 5;
const int BUZZERPIN = 12;

// Variables
boolean lockBox = false;
bool newLockBox = true;

// LCD variables
char lcdNextCoffee[32];
char lcdFinishedCoffee[32];

// Time limit
const int eatBeforeHour = 23;
const int eatAfterHour = 0;
const int eatDurationMinute = 1;
const int eatIntervalMinute = 2;
DateTime lastChocolateTime;

// Setup Helper functions

// loadSetup sets up the load cell and calibrates it
void loadSetup() {
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(2000.f);
  scale.tare();    
}

// lcdSetup sets up the lcd to have backlight
void lcdSetup() {
  screen.init();
  screen.begin(20,4);
  screen.backlight();
  screen.home();
}

// LCD Helpers

// lcdChocolateTime is the LCD display for when the box unlocks
void lcdChocolateTime() {
  screen.clear();
  screen.setCursor(0, 0);
  screen.print("I T ' S");

  screen.setCursor(0,1);
  screen.print("  C O F F E E");
  screen.setCursor(0,2);
  screen.print("    O ' C L O C K");

  screen.setCursor(0,3);
  screen.print("   * e n j o y *");
}

// tooManyChocLcd is the LCD display when users take more than 1 bar of chocolate
void tooManyChocLcd() {
  Serial.println("TOO MANY!!");
  screen.clear();
  screen.setCursor(0, 0);
  screen.print("Y O U    T O O K");

  screen.setCursor(0,1);
  screen.print("  T O O    M A N Y");
  screen.setCursor(0,2);
  screen.print(" C H O C O L A T E S");

  screen.setCursor(0,3);
  screen.print("  * s u f f e r *");
}

// lcdDisplay is the generic display when the box id locked
void lcdDisplay(DateTime datetime) {
  screen.clear();
  screen.setCursor(0, 0);
  sprintf(t, "%02d/%02d/%02d  %02d:%02d:%02d", datetime.month(), datetime.day(), datetime.year(), datetime.hour(), datetime.minute(), datetime.second());
  screen.print(t);

  // calculate when is the next time to unlock the box
  DateTime nextChocolateTime = lastChocolateTime.unixtime() + eatIntervalMinute * 60;
  screen.setCursor(0, 2);
  sprintf(lcdNextCoffee, "COFFEE CHOC @ %02d:%02d!", nextChocolateTime.hour(), nextChocolateTime.minute());
  screen.print(lcdNextCoffee);
}

// maybeLockBox checks for whether to lock the box
void maybeLockBox(DateTime datetime) {
  boolean isBeforeHour = (datetime.hour() <= eatBeforeHour);
  boolean isAfterHour = (datetime.hour() > eatAfterHour);

  if (isBeforeHour and isAfterHour and datetime.minute() % eatIntervalMinute == 0) {
    newLockBox = false;
  } else {
    newLockBox = true;
  }
}

void setup() {
  steeringMotor.attach(MOTORPIN);
  pinMode(MAGNETPIN, INPUT_PULLUP);
  pinMode(BUZZERPIN, OUTPUT);

  Wire.begin();
  rtc.begin();
  datetime = rtc.now();
  lastChocolateTime = DateTime(datetime.year(), datetime.month(), datetime.day(), datetime.hour(), int(datetime.minute()/2) * 2, 0);

  lcdSetup();
  loadSetup();
  
  Serial.begin(9600); // communication rate
  Serial.print("SETUP");
}

void loop() {
  Serial.print(scale.get_units(), 1);
  
  datetime = rtc.now();
  
  maybeLockBox(datetime);

  if (newLockBox != lockBox) {
      // only move servo if a state has changed
      if (newLockBox) {
        steeringMotor.write(180);
      } else {
        steeringMotor.write(90);
        // make sure buzzer is not on when user opens the box
        noTone(BUZZERPIN);
        lcdChocolateTime();
        lastChocolateTime = datetime;
        
        int prevWeight = scale.get_units();
        delay(eatDurationMinute * 60000);
        int currWeight = scale.get_units();
        Serial.println(currWeight - prevWeight);

        // check whether more than one bar of chocolate is taken
        if (prevWeight - currWeight > 50) {
          Serial.println("choco thieffff!");
          tone(BUZZERPIN, 800); 
          tooManyChocLcd();
          delay(2000);
        }
        
      }

      lockBox = newLockBox;
  }
  
  noTone(BUZZERPIN); // set default to no buzzing
  
  if (digitalRead(MAGNETPIN) == HIGH and lockBox){
    tone(BUZZERPIN, 330); 
  } else {
    Serial.println("CLOSED");
  }

  lcdDisplay(datetime);

  delay(1000);
}

 

 

/*
 * Project 2: stop snac-ing habit helper
 * Zoe Teoh
 * 
 * Description:  
 * 
 * this is the code for habit helper
 * 
 * habit helper is a physical data 
 * visualization of the intake of caffeine over a week.
 * 
 * 
 * Pin mapping:
 * 
 * pin   | mode   | description
 * ------|--------|------------
 * 3       output   monday LED
 * 4       output   tuesday LED
 * 5       output   wednesday LED
 * 6       output   thursday LED
 * 7       output   friday LED
 * 8       output   saturday LED
 * 9       output   sunday LED
 * 40      input    momentary push button
 * 41      input    momentary push button
 * 
 */
 
// IMPORTS
#include <RTClib.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// PIN MAPPING

const int MON = 3;
const int TUE = 4;
const int WED = 5;
const int THU = 6;
const int FRI = 7;
const int SAT = 8;
const int SUN = 9;

const int COFFEEBUTTON = 40;
boolean coffeePressed = false;

// DEBUG - CHANGING DAY
const int TIMEBUTTON = 41;
boolean timePressed = false;

boolean weeklyReset = false;


// GENERAL CONSTANTS

// Real Time Clock
RTC_DS3231 rtc;
char t[32];

// Coffee intake
char currentLCD[32];
int currentCoffee = 0;
int limitCoffee = 3;
DateTime currentCoffeeDay = 0;

// LCD
LiquidCrystal_I2C screen(0x27, 20, 4);

// DATA STORAGE
int habit_data[31] = {};

// SETUP HELPER FUNCTIONS

void ledSetup() {
  pinMode(MON, OUTPUT);
  pinMode(TUE, OUTPUT);
  pinMode(WED, OUTPUT);
  pinMode(THU, OUTPUT);
  pinMode(FRI, OUTPUT);
  pinMode(SAT, OUTPUT);
  pinMode(SUN, OUTPUT);
}


void lcdSetup() {
  screen.init();
  screen.begin(20,4);
  screen.backlight();
  screen.home();
}

void clockSetup() {
  Wire.begin();
  rtc.begin();

  DateTime dt = rtc.now();
  currentCoffeeDay = getStartOfDay(dt);
}


// GENERAL HELPER FUNCTIONS

void showTime(DateTime datetime) {
  screen.setCursor(0, 0);
  sprintf(t, "%02d/%02d/%02d  %02d:%02d:%02d", datetime.month(), datetime.day(), datetime.year(), datetime.hour(), datetime.minute(), datetime.second());
  screen.print(t);

  screen.setCursor(0, 2);
  screen.print("Limit: 3 cup coffee");
  screen.setCursor(0, 3);
  sprintf(currentLCD, "Curr:  %01d cup coffee", currentCoffee);
  screen.print(currentLCD);
}

void dailyLED(DateTime datetime) {

  // whenever button is pressed, currentCoffee increments
  currentCoffee += 1;

  const int WEEKDAY_NAMES[7] = {SUN, MON, TUE, WED, THU, FRI, SAT};
  int weekdayNum = datetime.dayOfTheWeek();
  int weekdayPin = WEEKDAY_NAMES[weekdayNum];

  // checking whether to light up LED
  if (currentCoffee > limitCoffee) {
    digitalWrite(weekdayPin, HIGH);
    habit_data[datetime.day()] = 1;
  }
}

//button changing of day
void changeDay(DateTime datetime) {
  DateTime newDateTime = (datetime.unixtime() + 86400L);
  rtc.adjust(newDateTime);
}

// HELPER HELPER FUNCTION

int getArraySliceSum(const int temp_array[], int start_index, int end_index) {
  int sum = 0;
  for (int i = start_index; i < end_index; i ++ ) {
    sum += temp_array[i];
  }
  return sum;
}

void turnOffDailyLights() {
  for (int i = MON; i <= SUN; i ++) {
    digitalWrite(i, LOW);
  }
}

DateTime getStartOfDay(DateTime dt) {
  return DateTime(dt.year(), dt.month(), dt.day(), 0, 0);
}

void setup() {
  Serial.begin(9600);
  Serial.println("SETUP");
  
  pinMode(COFFEEBUTTON, INPUT);
  pinMode(TIMEBUTTON, INPUT);
  
  ledSetup();
  lcdSetup();
  clockSetup();
  Serial.println("SETUP DONE");
}

void loop() {
  DateTime now = rtc.now();
  
  showTime(now);
  
  if (digitalRead(TIMEBUTTON) == HIGH) {
    if (not timePressed) {
      changeDay(now);
    }
    timePressed = true;
  } else {
    timePressed = false;
  }

  // reset currentCoffee if it's new day
  if (now.unixtime() - currentCoffeeDay.unixtime() > 86400L) {
    currentCoffeeDay = getStartOfDay(now);
    currentCoffee = 0;
  }

  // resetting the week
  if (now.dayOfTheWeek() == 1) {
    if (not weeklyReset) {
      turnOffDailyLights();
      weeklyReset = true;
    }
  } else {
    weeklyReset = false;
  }

  // long click is still one click
  if (digitalRead(COFFEEBUTTON) == HIGH) {
    if (not coffeePressed) {
      dailyLED(now);
    }
    coffeePressed = true;
  } else {
    coffeePressed = false;
  }

  // LCD THINGS
  showTime(now);
}