By Sharon Li

The corrective lenses tracker and holder allow an individual to store their contact lenses and glasses in the two separate compartments in addition to serving as a tracking device for how long weekly contact lenses have been opened.

Demonstration of the corrective lens tracker and holder in demo mode where 24 hours is converted to 10 seconds and 5-day use contact lens. It is also sped up 4x.

Overview Photo

Inside the main compartment of the box where you store your contact lens, there is an IR sensor on the side that detects whether or not the lid is closed, which is used to update the LED display.

In this box, there is a false floor that hides all of the hardware underneath so that the user cannot see it.

To the right of the box, there is a side compartment whose intention is to hold or store glasses when not in use.

A user can open the lid to update the contact lens tracker information on the LED display automatically if it hasn’t already been updated that day. This tracker would update automatically at 11:59 pm even if the user did not put their contacts in that day.

Hypothetically if the user were to lose their contacts or for whatever reason lose their pair of contacts and need to open a new pack, the user can hold down the button for 3 seconds to reset the “life” of the contacts tracker. This 3-second hold will also apply when it is time to change contacts.

Process Images and Review

During the beginning stages of developing my idea, I had a lot of components and functions on this box that didn’t provide any supplementary support but rather were just extra embellishments for design. As a result, I decided to simplify my box more for easier use and decided to add an extra side component to store my glasses since I have more than one corrective lens. I reduced the number of buttons to one for a much cleaner look and to have the display on the entire time. Furthermore, I was able to add additional parts to my box for better functionality, such as a real-time clock that can track the days as well as an IR sensor to track if the lid was closed or not. 

Initial ideation sketches of this tracker on the top and holder with a further and more finalized design of my sketch on the bottom after consulting with Professor Zacharias.

Following this, Professor Zacharias was able to help me with the CAD design of this box for the final product and all of its features. Specifically, I was able to construct the false floor of my project so that it could hide all of the hardware, and users would be able to operate this box seamlessly and would not see any of the wires or the Arduino. It was difficult to decide on the dimensions of this box without knowing how I was able to fit all of the hardware inside the false floor. Therefore, I had to estimate and allot more space than needed in the case where it might not fit entirely. 

Final CAD design of the corrective lens holder and tracker with the help of Professor Zacharias.

While assembling the product after laser cutting the plywood using the CAD design, it was arduous to make sure that the code was working well. Meaning that I had to figure out the logistics of how I wanted this box to operate. Specifically, how I wanted the lid to function and how it would work in terms of tracking the days. As a result, I decided that the box would update itself at the end of the day on a daily basis even if the contacts were not used because my contacts are for a 7-day use regardless if I use them every day or not. I also had to do research here since initially, I did not know it was only for 7-day use, so I had to figure out a way to accommodate the fact that it can only be used for this certain time period. To solve this, I decided on the box updating automatically every night. By opening the lid, it would update automatically so users can see the most recent number of how many days the contacts have been opened. 

Assembling and wiring the hardware of my corrective lens holder as well as setting up all the components individually and then eventually altogether.

Building and attaching the laser-cut pieces of plywood together and mounting the LED display as well as the button to the final prototype.

While assembling the product after laser cutting the plywood using the CAD design, it was arduous to make sure that the code was working well. Meaning that I had to figure out the logistics of how I wanted this box to operate. Specifically, how I wanted the lid to function and how it would work in terms of tracking the days. As a result, I decided that the box would update itself at the end of the day on a daily basis even if the contacts were not used because my contacts are for a 7-day use regardless if I use them every day or not. I also had to do research here since initially, I did not know it was only for 7-day use, so I had to figure out a way to accommodate the fact that it can only be used for this certain time period. To solve this, I decided on the box updating automatically every night. By opening the lid, it would update automatically so users can see the most recent number of how many days the contacts have been opened. 

Testing the IR sensor for functionality in conjunction with the LED display and that it would update simultaneously upon “opening” and “closing” the lid, which in this case was my hand.

During this process, I encountered many difficulties and problems when writing the code, as it would not show up on the display or the timing would not match up, and it would continue to count even after 7 days. To solve this problem, I added many booleans to track all the different variables that I didn’t expect to consider earlier.

A snippet of my demo code used for the final prototype where many booleans had to be considered in order for the box to update based on certain facts and the LED display would correctly display as well.

Discussion

Despite my initial concern throughout the ideation process that I would not be able to brainstorm a viable product that would ultimately be useful for me, I am proud of my progress throughout this process. Especially seeing my project literally come out of the lines in my sketches into an actual and tangible product is lively. I satisfied my goals for this project since I was able to target a reoccurring problem I have with keeping track of my contacts and finding a place to store my glasses, so it’s like hitting two birds with one stone. It was super helpful to receive feedback from Professor Zacharias and my peers during the ideation process, and it helped me look at my project from a different perspective.

However, some improvements could be made to my project after seeing the final product. Specifically, during the final critique where my peer commented, “Portability if you want to bring it when you travel? Like is it possible to make it a bit flatter? Do you need such a big screen?”. I agree with this comment, as it was one of my biggest concerns while prototyping and figuring out the measurements. Specifically, it was difficult to estimate the space needed for the hardware. I would have loved to make it smaller so it would not take up as much space in one’s room, especially since the contact lens case is usually the size of an eraser. Another comment I agreed with was, “ What if you have contacts that are monthly/yearly? How easy would it be to change it?”. Adding more features to my box would have been helpful and added to its functionality. Since this box was tailored to me, I did not need to think about the lens past the seven-day use. However, it could be helpful to have a feature that could manually change the days the contacts can be used or, more generally, track any item that could be used.

After this project, I would definitely build another iteration of this project that heavily revolves around the concerns I had that were directly related to the critiques I received. For example, I would make the box smaller and more compact by using another microcontroller and a smaller LCD display. In addition, I would definitely try to add more to the programming side and allow users to change the days, amongst other things. Overall, I learned that I had difficulty coding this project, specifically integrating a time aspect into it. Furthermore, I enjoyed the hands-on aspect of building the project and laser cutting it, particularly the ideation portion of coming up with this idea and sketching it.

Functional Block Diagram and Schematic

Code

/*
  Corrective Lens Tracker and Holder
  Sharon Li

  The code below keeps track of the duration of time a pack of contact lens has
  been open for using real-time data information from a real time clock that is
  displayed on a LCD display. This tracker will update once every day either
  manually by the user when they open the lid of the box for the first time that
  day or automatically at 11:59PM. After 7 days (the maximum number of days a pair
  of contacts can be used for), the LCD display will warn users to open a new
  pair of contacts.

  The code below additionally includes a demo mode where 24 hours is converted to
  10 seconds.

  Pin Map:
  Pin | role | Description
  ----------------------------------------
  2 | input | Arcade Push Button input
  A0 | input | IR Proximity Sensor input
  SCL | input | SCL pin on Real Time Clock (DS3231)
  SDA | input | SDA pin on Real Time Clock (DS3231)
  A4 | input | SCL pin on I2C LCD display
  A5 | input | SDA pin on I2C LCD display
  5V | output| VL53L0X sensor power
  GND | input | Ground for all components

  Code Credit:
  RTC DS3231 code sourced from the DS3231 library example code.
  IR Proximity Sensor code sourced from the NewPing library example code.
  isLeapYear and changing from date format to day of the year code (Lines 65-66 & 102-109)
  sourced from https://stackoverflow.com/questions/19110675/calculating-day-of-year-from-date.

*/


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

#include <DS3231.h>
DS3231  rtc(SDA, SCL);

const int IRSENSOR = A0;
const int BUTTONPIN = 2;

int contactsLife = 0;

bool lidReset = true;
bool dayUpdated = false;
bool oldContacts = false;

unsigned long buttonStart;
unsigned long elapsedTime;
unsigned long regTimer;
unsigned long timeChecker;
int resetButton;

Time  t;

int month;
int year;
int day;

bool isLeapYear(int year) {
  return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}

void setup() {
  pinMode(IRSENSOR, INPUT);
  pinMode(BUTTONPIN, INPUT);
  Serial.begin(9600);

  screen.init();
  screen.backlight();

  rtc.begin();

  ////   The following lines can be uncommented to set the date and time
  //    rtc.setDOW(TUESDAY);     // Set Day-of-Week to SUNDAY
  //    rtc.setTime(9, 49, 00);     // Set the time to 12:00:00 (24hr format)
  //    rtc.setDate(, 11, 2022);   // Set the date to January 1st, 2014

}

void loop() {
  /******************************************************************************************************
      Code for updating the tracker using the IR Proximity Sensor or automatically and 7-day warning
  ******************************************************************************************************/
  //  Get data from the IR Proximity Sensor
  int lidSensor;
  lidSensor = analogRead(IRSENSOR);
  delay(1000);

  // Get data from the DS3231
  t = rtc.getTime();
  month = t.mon;
  day = t.date;
  year = t.year;

  // 2d table mapping the days of the year to each month
  int daysToMonth[2][12] =
  {
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 },
  };

  // function that converts the date format to the day of the year
  int dayOfYear = daysToMonth[isLeapYear(year) ? 1 : 0][month - 1] + day;

  int contactsStart; //the constant date when the contacts are opened
  int startChecker = true; // if the start date is stored or not
  int contactsCurr = 0; //the most recent checked date
  int dayChecker; //checks what day it is

  if (startChecker) { // if it is the start date
    contactsStart = dayOfYear; // sets the start date to the current date
    startChecker = false;
  }

  if (lidSensor <= 50 and lidReset and not dayUpdated and not oldContacts) { // adds one day if lid opened
    lidReset = false;
    contactsLife = contactsLife + 1;
    dayUpdated = true;
  }

  if (dayUpdated == false and contactsCurr - dayChecker >= 1 and not oldContacts) { // adds one day automatically
    contactsLife = contactsLife + 1;
    dayUpdated = true;
  }

  if (contactsCurr - dayChecker >= 1 and not oldContacts) { // checks if it has been one day
    dayUpdated = false;
    timeChecker = regTimer;
  }

  ////   The following lines can be uncommented to start DEMO MODE and comment lines 111-135
  //  regTimer = millis();
  // if (lidSensor <= 50 and lidReset and not dayUpdated and not oldContacts) { // adds one day if lid opened
  //    lidReset = false;
  //    contactsLife = contactsLife + 1;
  //    dayUpdated = true;
  //  }
  //
  //  if (dayUpdated == false and regTimer - timeChecker > 9000 and not oldContacts) { // adds one day automatically
  //    contactsLife = contactsLife + 1;
  //    dayUpdated = true;
  //  }
  //
  //  if (regTimer - timeChecker >= 10000 and not oldContacts) { // checks if it has been 10 secs
  //    dayUpdated = false;
  //    timeChecker = regTimer;
  //  }

  if (lidSensor > 50 and not lidReset and not oldContacts) { // closed lid
    lidReset = true;
  }


  if (contactsLife >= 7) { // show warning message after it has been 7 days
    oldContacts = true;
    screen.clear();
    screen.home();
    screen.print("Time For New Contacts!");

    while (resetButton == LOW) { // if the button is pressed to reset, resets the tracker
      resetButton = digitalRead(BUTTONPIN);
    }

    contactsLife = 0;
    oldContacts = false;
    startChecker = true;
    delay(1000);
    resetButton = LOW;
  }

  /******************************************************************************************************
       Code for arcade button functions and LCD display resets
  ******************************************************************************************************/

  resetButton = digitalRead(BUTTONPIN);

  Serial.println(resetButton);

  if (resetButton == HIGH and buttonStart == 0) { // starts timer when button is pressed
    buttonStart = millis();
  }

  if (resetButton == HIGH and buttonStart != 0) { // pressed button and timer is on
    resetPrint();
    elapsedTime = millis();
    if (elapsedTime - buttonStart >= 3000) {
      buttonStart = 0;
      contactsLife = 0;
      oldContacts = false;
      screen.clear();
      defaultPrint();
    } else {
      defaultPrint();
    }
  }

  if (resetButton == LOW) { // when button is not pressed
    defaultPrint();
    clearPrint();
  }
}

// function that displays the reset screen on the LCD display
void resetPrint() {
  screen.setCursor(0, 2);
  screen.print(String("Hold for 3 seconds"));
  screen.setCursor(0, 3);
  screen.print(String("to reset"));
}

// function that displays the default screen on the LCD display
void defaultPrint() {
  screen.home();
  screen.print(String(" Contacts Life Span:"));
  screen.setCursor(7, 1);
  screen.print(contactsLife + String(" day(s)"));
}

// function that clears the screen on the LCD display due to some bugs
void clearPrint() {
  screen.setCursor(0, 2);
  screen.print(String("                  "));
  screen.setCursor(0, 3);
  screen.print(String("        "));
}