A device to help lock away your phone for a specified amount of time so you can focus on tasks at hand.

An overall photo for portion and scale.

The “self destruct” button

The LED strip on a wall of the box.

The Hour and Minute dials as well as the Set button.

The use of the LCD screen. This shows the main feature which is the Set Timer feature, it also shows the top unlocked (i.e. the servo lock is down)

Process Images and Review

This was a decision point when I determined that two rotary encoders was better than one. This was a big decision because I always intended to use 1 and utilize the button feature on the encoder to switch between hours and minutes – but I realized that using 2 was much more efficient.

self destruct button

This was a major decision point. Due to how hard it was for me to get the timer to work properly, I had originally scrapped the idea of a self destruct button. However, I really wanted to cover the use case of me needing my phone before time was up so I took a leap even though I was low on time – and included the self-destruct button/all of it’s code.

first process image with components i intended to include

This is right when I started the assignment. The goal at this point was to get all of the components functioning on the Arduino singularly and then to have them signal to each other.

At this point, all of the components worked (servo, LED strip, etc.) except the timer didn’t function properly so nothing worked “together”.

This is after Zach walked me through the logic of the timer and I was finally able to code it so it functioned properly.

two walls of the box built and trying to fit wiring inside

This was the point at which the electrical components all worked, and the box had been laser cut, and the biggest concern was unwiring and rewiring everything to fit nicely inside the box, as well as getting the box to stay together as I did that.

Discussion

The in-class critiques that I got were mostly just positive attributes that classmates appreciated about the design. It didn’t really give me much to move forward with, but it was good to have my design decisions validated. One person commented, “I thought the name was very cool and I liked the use of a tactile button to control the timer.” I really liked the idea of the project and what I was making so I put a lot of time into designing based on what I would like to use. I’ve always wanted an “inator” so it seemed like a perfect time to create something like that… thus the name. Another person commented “It looks like a lot of the components of your project are working really well together. I also like the LED use”. When conceptualizing this assignment, I wanted to create something simple enough that I could do everything I needed to do within the ~two week span we were allocated. Thus, all of the components of my project – the LED strip, the LCD screen and timer, the self-destruct button… It was all in an effort to finish the project on time and create something I’d want to use. So I appreciated this comment because I sought to be very intentional. 

This project was unexpectedly challenging. I anticipated a walk in the park on a day with old snow (fumbling/tripping on dirty snow, a bit of slipping on ice – but ultimately being fine), but instead my casual walk included me getting wrapped up in a tornado and spit out at the end of the park. Thus, all of the components (LED strip, buttons/dials, etc.) came together somewhat easily and overall didn’t take more time than anticipated… but the timer was the tornado. I didn’t expect the logic and coding of it to be some math-heavy and difficult. I worked an entire weekend without getting anywhere, and restarted the code from scratch twice to make it work. In retrospect, I would tell myself to not overthink the logic and to go down the math route rather than scenario planning for each individual component of the timer (hour, min, sec). I also wish I hadn’t gotten too hung up on the timer – I think it would have benefitted me more to move onto the actual box/laser cutting while I waited and tried to debug the timer. However once the timer was completed, all the other components came together within two days and there were no more major storms on my walk.

Overall, I’d say I’m very proud of this project. It satisfied all of my goals and created a project that I honestly intend to use for years to come. In fact, I’d say I satisfied my goals almost to a fault. After seeing other people’s projects – I realized I could have gone even further. For example, I have a lamp that has 3 slots for USB’s, so I didn’t include a battery pack or an on/off button on the Nay-Phone Inator, instead I just left it for a USB input because that is what fit my specific needs. Also things like what happens after the self destruct button is pushed. The idea was that after the self-destruct button and phone retrieval, you simply unplug the USB (because why would the device need to be on if you had given up?), but in the final critique some people seemed surprised that there was no way to return to the Set Timer screen after the phone was retrieved. So I think small things like that would actually be my Next Steps. I don’t plan to build another iteration of the project, but if I did – I would address those small details.

Technical Information

/*
Nay Phone Inator - Project 2
Noni Shelton
Code for a device that locks away your phone for a specified amount of time so you can focus on tasks at hand.
Credit to Professor Zach 
Credit to the creators of the libraries I used 
*/

/*
    PIN MAPPING
  pin   | mode   | description
  ------|--------|------------
  2      input    Rotary Encoder - hour
  3      input    Rotary Encoder - hour
  4      input    The lock (the Servo motor)
  5      input    The "Set" button
  13     input    The "self destruct" button
  9      input    Rotary Encoder - minute
  10     input    Rotary Encoder - minute

*/

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Encoder.h>
#include <PololuLedStrip.h>
#include <Servo.h>
Servo lockMotor;

const int SELF_DESTRUCT_BUTTON_PIN = 13;
const int SET_BUTTON_PIN = 5;
const int LOCKMOTORPIN = 4;
unsigned long lcdTimer = 0;
unsigned long ledStripTimerSinglecolor = 0;
PololuLedStrip<6> ledStrip;

// LED STRIP: Create a buffer for holding the colors (3 bytes per color).
#define LED_COUNT 60
rgb_color colors[LED_COUNT];

int totalTimerSeconds = 0;
int selfDestructButtonState = 0;
int destructTimer = 0;
bool timerCounting = true;
bool countdownMode = false;
bool doorLocked = false;
bool endScreenPrintSequence = false;
bool destructPassed = false;

LiquidCrystal_I2C screen(0x27, 16, 2);
Encoder knobHour(2, 3);
Encoder knobMinute(9, 10);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  //initalizing pins
  pinMode(SELF_DESTRUCT_BUTTON_PIN, INPUT);
  lockMotor.attach(LOCKMOTORPIN);

  //inital timer
  Serial.begin(9600);
  screen.init();
  screen.backlight();
  screen.home();
  screen.clear();

  //intro screen
  screen.print("Nay-Phone Inator");
  delay(2000);
  screen.clear();

  //set timer
  screen.home();
  screen.print("Set Timer");
  screen.setCursor(5, 1);
  screen.print("00hr:00min");

}

void loop() {

  //setting up the rotary encoders
  int setTimerHour, setTimerMin;
  setTimerHour = knobHour.read() / 4;
  setTimerMin = knobMinute.read() / 4;

  //hour rotary encoder goes to 12 and loops around
  if (setTimerHour < 0) setTimerHour = 10;
  else if (setTimerHour > 10) setTimerHour = 0;

  //min rotary encoder goes to 12 and loops around
  if (setTimerMin < 0) setTimerMin = 60;
  else if (setTimerMin > 59) setTimerMin = 0;


  //bool sure set timer only prints once
  if (endScreenPrintSequence == false) {

    //printing the "Set Timer" changing values
    if (millis() - lcdTimer >= 250) {
      screen.clear();
      screen.home();
      screen.print("Set Timer");
      screen.setCursor(5, 1);
      screen.print("00hr:00min");

      //setting hours
      if (setTimerHour < 10) screen.setCursor(6, 1);
      else screen.setCursor(5, 1);
      screen.print(setTimerHour);

      //setting mins
      if (setTimerMin < 10) screen.setCursor(11, 1);
      else screen.setCursor(10, 1);
      screen.print(setTimerMin);

      lcdTimer = millis();
    }
  }

  //LED STRIP: making the LED Strip Flash a rainbow if it's in SetTimer Mode
  if (countdownMode == false) {

    //timer updates every 1/4th of a second
    if (millis() - lcdTimer >= 250) {

      // Update the colors.
      uint16_t time = millis() >> 2;
      for (uint16_t i = 0; i < LED_COUNT; i++)
      {
        byte x = (time >> 2) - (i << 3);
        colors[i] = hsvToRgb((uint32_t)x * 359 / 256, 255, 255);
      }

      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
    }
  }


  //set button pressed -- i.e. moving on to the countdown timer
  if (digitalRead(SET_BUTTON_PIN) == HIGH) countdownMode = true;

  if (countdownMode) {

    //updates every 1 second
    if (millis() - lcdTimer >= 100) {
      screen.clear();
      screen.home();
      screen.print("Timer");
      screen.setCursor(4, 1);
      screen.print("00:00:00");
      lcdTimer = millis();

      if (timerCounting) {
        totalTimerSeconds = ((setTimerHour * 3600) + (setTimerMin * 60));
        timerCounting = false;
      }

      //every second, the hours/mins/seconds are updated
      totalTimerSeconds--;
      //updates seconds
      int seconds = totalTimerSeconds;
      //updates hours
      int hourUpdate = seconds / 3600;
      seconds = seconds - (hourUpdate * 3600);
      //updates minutes
      int minUpdate = seconds / 60;
      seconds = seconds - (minUpdate * 60);

      if (totalTimerSeconds >= 0) {

        //servo motor: locking the door --
        lockMotor.write(5);

        //led strip turns red
        for (uint16_t i = 0; i < LED_COUNT; i++) {
          colors[i] = rgb_color(255, 0, 0);
        }

        //Updating the timer on the LCD Screen
        //printing the seconds
        screen.setCursor(10, 1);
        if (seconds < 10) screen.setCursor(11, 1);
        else screen.setCursor(10, 1);
        screen.print(seconds);

        //printing the mins
        if (minUpdate < 10) screen.setCursor(8, 1);
        else screen.setCursor(7, 1);
        screen.print(minUpdate);

        //printing the hours
        if (hourUpdate < 10) screen.setCursor(5, 1);
        else screen.setCursor(4, 1);
        screen.print(hourUpdate);

        //SELF DESTRUCT MODE
        selfDestructButtonState = digitalRead(SELF_DESTRUCT_BUTTON_PIN);

        if (selfDestructButtonState == HIGH) {

          //setting a destruct countdown
          destructTimer++;

          //making sure the screen prints the proper thing
          endScreenPrintSequence = true;

          if (destructTimer < 10) {

            //destruct timer displays warning
            screen.clear();
            screen.print("Hold for 10 secs");
            screen.setCursor(0, 1);
            screen.print("to give up");
            delay(1000);
          }

          else {

            //passing the destruct bool
            destructPassed = true;
          }
        }

        if (destructPassed) {

          //destruct timer displays disappointment
          screen.clear();
          screen.print("Dang. That was");
          screen.setCursor(0, 1);
          screen.print("disappointing.");
          delay(1000);

          //servo motor: unlock the door

          lockMotor.write(90);
          delay(1000);

        }

      }

      else {
        //TIMER HAS BEEN ENDED

        //making sure the screen prints the proper thing
        endScreenPrintSequence = true;

        screen.clear();
        screen.home();
        screen.print("Amazing job! You");
        screen.setCursor(0, 1);
        screen.print("rock homie");

        //servo motor: unlock the door

        lockMotor.write(90);
        delay(1000);

        //LED STRIP: updating the LED strip with congratulatory colors
        // Update the colors.
        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);
        }
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
    }
  }
}

// THE FUNCTIONS I'M USING

// Converts a color from HSV to RGB.
// h is hue, as a number between 0 and 360.
// s is the saturation, as a number between 0 and 255.
// v is the value, as a number between 0 and 255.
rgb_color hsvToRgb(uint16_t h, uint8_t s, uint8_t v)
{
  uint8_t f = (h % 60) * 255 / 60;
  uint8_t p = (255 - s) * (uint16_t)v / 255;
  uint8_t q = (255 - f * (uint16_t)s / 255) * (uint16_t)v / 255;
  uint8_t t = (255 - (255 - f) * (uint16_t)s / 255) * (uint16_t)v / 255;
  uint8_t r = 0, g = 0, b = 0;
  switch ((h / 60) % 6) {
    case 0: r = v; g = t; b = p; break;
    case 1: r = q; g = v; b = p; break;
    case 2: r = p; g = v; b = t; break;
    case 3: r = p; g = q; b = v; break;
    case 4: r = t; g = p; b = v; break;
    case 5: r = v; g = p; b = q; break;
  }
  return rgb_color(r, g, b);
}