Project 2 – Intro to Physical Computing: Student Work Fall 2022 https://courses.ideate.cmu.edu/60-223/f2022/work Intro to Physical Computing: Student Work Mon, 31 Oct 2022 17:57:10 +0000 en-US hourly 1 https://wordpress.org/?v=6.0.8 “Self-Watering” System https://courses.ideate.cmu.edu/60-223/f2022/work/self-watering-system/ Mon, 31 Oct 2022 06:24:33 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16686

 

“Self-Watering” System

Elise Chapman

 

Simple Description

This system rewards the user for “watering” themselves (pouring a glass of water) with a sweet, printed poem. This encourages the user to be cared for both mentally and physically.

 

Overview Images

Printing and taking a poem

Overview photo

Tech insert details

Where to plug in external power for printer

Output of printed “poem”

 

Process Images and Review

Initial drawings of concepts for project 2

Beginning my project, I knew I wanted to do something that was a little more abstract, but mostly was also mostly a vehicle for me to design something interesting. My goal was to do something that would be more challenging with physical build and hardware and not so challenging with software, to reflect my personal abilities. After some discussion with Zach, I decided to go with the poem printer. I thought that it would be something different, but still fun! The only thing I changed from my initial concept was to do the poem selection based on the weight class of a glass of water. This was a way for me to include another physical input form that I hadn’t tried before. Buttons are fine but I wanted to challenge myself a little more.

Creation thoughts and sketches, as well as some measurements for laser cutting

On the design concept side, I found a lot of my inspiration through just looking through the scrap pile in the laser cutting room of ideate. There was this great mirrored acrylic scrap that there was enough to work with, so I grabbed it! I had the idea to make the mirrored material to look like a puddle to reflect (no pun intended) the glass of water that was going to go in as a part of the build. I think this style of design turned out well and the final build feels polished, largely in thanks to the mirrored acrylic.

Freshly laser-cut mirrored acrylic, before I inserted the printer, speaker, and switch, and put it on its base

Two major points of my project were getting the load cell working and getting the printer to print poems out of an array.

beginning to work w load cell

Beginning of working with the load cell

Firstly, getting the load cell to work within my final model was quite tricky. Getting it to work in general was fine, but that was in the context of standard tables and the lab, aided with clamps. That came back to bite me later as I was trying to insert the load cell into my final build. I assumed that I would be able to duct tape the load cell to the lid of my build, therefore creating enough tension for the load cell to function. In practice, however, that was not the case. I duct taped it in, but it was not calibrating correctly and not calibrating consistently.

Some of the strange outputs I was getting while trying to calibrate a non-clamped load cell

I then realized that I needed to provide more support to the one side of the load cell. So, I tried to add in vertical supports underneath by cutting and propping up some of the eighth-inch laser cutting wood. That didn’t really work. So, I decided that what I really needed was to recreate the clamp sensation within my build. So, I hauled myself to the design product studio to find blocks of wood and screws. I created a wooden base, which I screwed into the base of the build and then I screwed the load cell into that. This, finally, was enough to recreate the tension support of using a clamp on the load cell bar.

My triumph of getting the load cell to work

Then, my second challenge, which I still have not overcome: the printing poems out of an array. Originally, the problem was that I had very little to paper to test on, so I did most of my initial programming with Serial print in mind. However, this part of the problem was resolved when the printer paper came in.

The more difficult and ongoing issue for printing the poems is memory. Unfortunately for me, the Arduino has very little memory, so even something as small as a haiku takes up far more memory than that little thing has. Therefore, when trying to print poems out of an array, I simply can’t. For reasons I don’t know, even trying to print single words out of an array seemed to be posing an issue for my system. The printer can print cohesivelyIt’s rather frustrating because printing the poem is the crux of my device—without it, it seems to do nothing at all besides make some noise. Sometimes, my printer will print seemingly random characters, ones that aren’t even a part of the poems; meaning that it’s reading something out of the arrays, just I’m not sure what. All in all, it’s something I am still working to fix.

Some code from my most recent help session with Zach

To try and remedy this I have met with Zach two times now, to very little success. We added in an microSD card in hopes that by having the Arduino pull from text files, it will bypass the memory issue. So far, we have been able to pull information out of the text files on the microSD card, but transferring that information to the printer still seems to be an issue. The last we left off, the printer could print parts of my poems, but not the entire poem. Once again, it’s following some form of logic that I personally can’t follow. Although this project is over for the class, I plan to continue working on this project. I really want to see it through. Despite how frustrated it makes me, I still think the concept is interesting and I want to have a piece that I can show the world.

 

Discussion

This has been quite a difficult project for me. I think what has proved quite a challenge has been the things I did not expect to be a challenge at all, doubly so because they seemed to appear last-minute, but not have been easy to solve at all. 

Some things, of course, did go easily: like the design. I’m happy with how the design turned out visually. It looks clean but still interesting and I think that people respond to the mirror acrylic in a way I hoped they would. I wanted to create something playful but still sophisticated. For example, in the in-class crit, I got good positive feedback including “very aesthetically pleasing”, “the design was well executed”, and “the laser cut mirror acrylic panels on the top were really effective”. My design was the biggest thing I felt confident about and it was nice to see that reflected. Also importantly to me, people in the in-class crit seemed to understand my intent with the project, despite its flaws. My favorite comment was: “I love the meaning and thought behind the project. I also love that the poem is printed and feels more meaningful than just seeing it printed on the screen”. I try to incorporate a materiality of being into my work, making things where you walk away with something, tangible or not. As I often describe it, I like making my work something to be existed with, not just tangentially to. It made me feel proud that others can see my work in the same way that I see my work. As a designer, I often worry about communicating the things in the way I intend to, so especially on abstract projects like this one I am not sure my message is getting through. But it really seems like it is, and I’m glad for it. 

But on this project’s flaws, it has one main large one: The poems will not print properly. Like I mentioned before, this is incredibly frustrating because the poem printing is sort of the crux of the machine, it’s sort of its point. I didn’t even expect the poem printing to be an issue. I didn’t know about the Arduino’s limited memory going into the project, so I simply assumed that pulling out an array of strings would be easy. I’ve coded with arrays before so it felt like familiar territory. In fact, I assumed that the harder part would be to get the printer printing in general. Not so much. It really is something that I’m still working towards fixing and I do want it fixed eventually. I don’t think I’ll really step back from this project until it feels like a total dead end. 

Overall, I am so-so on this project. On one hand, I am happy with the visual appearance of the design. It’s self-contained and pretty; it’s something I wouldn’t mind having on my desk or on my night stand, which was the goal. It satisfied my desire to try to design an object totally independently of a class. It’s a good accomplishment for me. On the other hand, I would be lying to say it works. The poems will not print properly. It’s something I’m really disappointed about. I’m frustrated with my inability to get it working, but I’m reminding myself that I can’t even get it working with Zach’s help, who is far more experienced with Arduino and code than I am. I stand by the concept, but if I could go back and tell four-weeks-younger Elise to just to do audio of reading the poems or something, I would.

 

Block Diagrams

Without the microSD card, presented on the final presentation day

With the microSD card

Schematic Diagram

 

Code (As it appeared on presentation day)

/*elise chapman
self-watering project
weight of a glass of water to printed poetry + a little jingle

pin mapping:
  load cell uses pins 2 and 3, as well as 1 GND and 2 PWR
  thermal printer uses pins 5 and 6, as well as 2 GND and 1 PWR
  speaker uses pin 8

parts of code sourced from:
  Nathan Seidle's HX711 Github Repository: https://github.com/sparkfun/HX711-Load-Cell-Amplifier/tree/master/firmware
  Adafruit's Thermal Printer library
  Arduino Project Hub's "How to make music with an Arduino" refrence
  Great assistance from Prof. Zacharias
*/

  
//load cell
#include "HX711.h"
#define calibration_factor -7050.0 //from the Calibration sketch
#define zero_factor 8421804 //from the Calibration sketch
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;
HX711 scale;

//printer & poems
#include "poems.h"
#include "Adafruit_Thermal.h"
#include "SoftwareSerial.h"
const int TX_PIN = 6;
const int RX_PIN = 5;
SoftwareSerial mySerial(RX_PIN, TX_PIN);
Adafruit_Thermal printer(&mySerial); 
long pickNum = 0;

//jingle
#include "pitches.h"

void setup() {
  Serial.begin(9600);
  
  //load cell
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
  scale.set_scale(-700000); //from the Calibration sketch
  scale.set_offset(879228); //Zero out the scale using a previously known zero_factor

  //printer
  pinMode(7, OUTPUT); digitalWrite(7, LOW);
  mySerial.begin(19200);  // Initialize SoftwareSerial
  printer.begin();  // Init printer (same regardless of serial type)
  printer.setFont('A');
  printer.setSize('S');

  //jingle
  pinMode (8, OUTPUT);
}

void loop() {
  //load cell
  Serial.print("Reading: ");
  Serial.print(scale.get_units(), 1); //scale.get_units() returns a float
  Serial.print(" lbs");
  Serial.println();

  //poem and printer
  if (scale.get_units()<=0.3) {
    printer.println(poems[0]);
  }
  else if (scale.get_units()>0.3 && scale.get_units()<=0.4) {
    pickNum=random(1,3);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.4 && scale.get_units()<=0.5) {
    pickNum=random(3,6);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.5 && scale.get_units()<=0.6) {
    pickNum=random(6,8);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.6 && scale.get_units()<=0.7) {
    pickNum=random(8,10);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.7 && scale.get_units()<=0.8) {
    pickNum=random(10,13);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.8 && scale.get_units()<=0.9) {
    pickNum=random(13,17);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>0.9 && scale.get_units()<=1) {
    pickNum=random(17,20);
    printer.println(poems[pickNum]);
  }
  else if (scale.get_units()>1) {
    pickNum=random(20,22);
    printer.println(poems[pickNum]);
  }
  
  //jingle
  tone(8,NOTE_F5);
  delay(250);
  tone(8,NOTE_G5);
  delay(250);
  tone(8,NOTE_A5);
  delay(250);
  tone(8,NOTE_G5);
  delay(250);
  tone(8,NOTE_A5);
  delay(250);
  tone(8,NOTE_F5);
  delay(400);
  tone(8,END);

//  //waits for 1 min before measuring the water again
//  delay(60000);
  delay(20000);
}

pitches.h

#define NOTE_B0 31

#define NOTE_C1 33

#define NOTE_CS1 35

#define NOTE_D1 37

#define NOTE_DS1 39

#define NOTE_E1 41

#define NOTE_F1 44

#define NOTE_FS1 46

#define NOTE_G1 49

#define NOTE_GS1 52

#define NOTE_A1 55

#define NOTE_AS1 58

#define NOTE_B1 62

#define NOTE_C2 65

#define NOTE_CS2 69

#define NOTE_D2 73

#define NOTE_DS2 78

#define NOTE_E2 82

#define NOTE_F2 87

#define NOTE_FS2 93

#define NOTE_G2 98

#define NOTE_GS2 104

#define NOTE_A2 110

#define NOTE_AS2 117

#define NOTE_B2 123

#define NOTE_C3 131

#define NOTE_CS3 139

#define NOTE_D3 147

#define NOTE_DS3 156

#define NOTE_E3 165

#define NOTE_F3 175

#define NOTE_FS3 185

#define NOTE_G3 196

#define NOTE_GS3 208

#define NOTE_A3 220

#define NOTE_AS3 233

#define NOTE_B3 247

#define NOTE_C4 262

#define NOTE_CS4 277

#define NOTE_D4 294

#define NOTE_DS4 311

#define NOTE_E4 330

#define NOTE_F4 349

#define NOTE_FS4 370

#define NOTE_G4 392

#define NOTE_GS4 415

#define NOTE_A4 440

#define NOTE_AS4 466

#define NOTE_B4 494

#define NOTE_C5 523

#define NOTE_CS5 554

#define NOTE_D5 587

#define NOTE_DS5 622

#define NOTE_E5 659

#define NOTE_F5 698

#define NOTE_FS5 740

#define NOTE_G5 784

#define NOTE_GS5 831

#define NOTE_A5 880

#define NOTE_AS5 932

#define NOTE_B5 988

#define NOTE_C6 1047

#define NOTE_CS6 1109

#define NOTE_D6 1175

#define NOTE_DS6 1245

#define NOTE_E6 1319

#define NOTE_F6 1397

#define NOTE_FS6 1480

#define NOTE_G6 1568

#define NOTE_GS6 1661

#define NOTE_A6 1760

#define NOTE_AS6 1865

#define NOTE_B6 1976

#define NOTE_C7 2093

#define NOTE_CS7 2217

#define NOTE_D7 2349

#define NOTE_DS7 2489

#define NOTE_E7 2637

#define NOTE_F7 2794

#define NOTE_FS7 2960

#define NOTE_G7 3136

#define NOTE_GS7 3322

#define NOTE_A7 3520

#define NOTE_AS7 3729

#define NOTE_B7 3951

#define NOTE_C8 4186

#define NOTE_CS8 4435

#define NOTE_D8 4699

#define NOTE_DS8 4978

#define END -1

poems.h

const char poems[][100] PROGMEM = {"Pour yourself something! Hydrate yourself :)",
                                "The Short Ones\nAtticus\n\nSometimes\nthe short poems\nare the hardest\nto write\nchange one word\n and the whole poem\navocados.",
                                "The Tiger\nNael,age 6\n\nThe tiger\nHe destroyed his cage\nYes\nYES\nThe tiger is out",
                                "Trees\nJoyce Kilmer\n\nI think that I shall never see\nA poem lovely as a tree.\nA tree whose hungry mouth is prest\nAgainst the earth’s sweet flowing breast;\nA tree that looks at God all day,\nAnd lifts her leafy arms to pray;\nA tree that may in summer wear\nA nest of robins in her hair;\nUpon whose bosom snow has lain;\nWho intimately lives with rain.\nPoems are made by fools like me,\nBut only God can make a tree.",
                                "A Fairy Song\nWilliam Shakespeare\n\nOver hill, over dale,\nThorough bush, thorough brier,\nOver park, over pale,\nThorough flood, thorough fire!\nI do wander everywhere,\nSwifter than the moon’s sphere;\nAnd I serve the Fairy Queen,\nTo dew her orbs upon the green;\nThe cowslips tall her pensioners be;\nIn their gold coats spots you see;\nThose be rubies, fairy favours;\nIn those freckles live their savours;\nI must go seek some dewdrops here,\nAnd hang a pearl in every cowslip’s ear.",
                                "Warning\nJenny Joseph\n\nWhen I am an old woman I shall wear purple\nWith a red hat which doesn’t go, and doesn’t suit me.\nAnd I shall spend my pension on brandy and summer gloves\nAnd satin sandals, and say we’ve no money for butter.\nI shall sit down on the pavement when I’m tired\nAnd gobble up samples in shops and press alarm bells\nAnd run my stick along the public railings\nAnd make up for the sobriety of my youth.\nI shall go out in my slippers in the rain\nAnd pick flowers in other people’s gardens\nAnd learn to spit.\nYou can wear terrible shirts and grow more fat\nAnd eat three pounds of sausages at a go\nOr only bread and pickle for a week\nAnd hoard pens and pencils and beermats and things in boxes.\nBut now we must have clothes that keep us dry\nAnd pay our rent and not swear in the street\nAnd set a good example for the children.\nWe must have friends to dinner and read the papers.\nBut maybe I ought to practice a little now?\nSo people who know me are not too shocked and surprised\nWhen suddenly I am old, and start to wear purple.",
                                "On the Ning Nang Nong\nSpike Milligan\n\nOn the Ning Nang Nong\nWhere the Cows go Bong!\nand the monkeys all say BOO!\nThere’s a Nong Nang Ning\nWhere the trees go Ping!\nAnd the tea pots jibber jabber joo.\nOn the Nong Ning Nang\nAll the mice go Clang\nAnd you just can’t catch ’em when they do!\nSo its Ning Nang Nong\nCows go Bong!\nNong Nang Ning\nTrees go ping\nNong Ning Nang\nThe mice go Clang\nWhat a noisy place to belong\nis the Ning Nang Ning Nang Nong!!",
                                "Lines on the Antiquity of Microbes\nStrickland Gillian\n\nAdam.\nHad ’em",
                                "Love After Love\nDerek Walcott\n\nThe time comes when, with elation\nyou will greet yourself arriving\nat your own door, in your own mirror\nand each will smile at\nthe other’s welcome,\nand say, sit here. Eat.\nYou will love again the stranger who was your self.\nGive wine. Give bread. Give back your heart\nto itself, to the stranger who has loved you\nall your life, whom you ignored\nfor another, who knows you by heart.\nTake down the love letters from the bookshelf,\nthe photographs, the desperate notes,\npeel your own image from the mirror.\nSit. Feast on your life.",
                                "How to Get There\nLeunig\n\nGo to the end of the path until you get to the gate.\nGo through the gate and head straight out towards the horizon.\nKeep going towards the horizon.\nSit down and have a rest every now and again,\nBut keep on going, just keep on with it.\nKeep on going as far as you can.\nThat’s how you get there.",
                                "Risk\nAnais Nin\n\nAnd then the day came,\nwhen the risk\nto remain tight\nin a bud\nwas more painful\nthan the risk\nit took\nto blossom.",
                                "Autumn\nT.E.Hulme\n\nA touch of cold in the Autumn night –\nI walked abroad,\nAnd saw the ruddy moon lean over a hedge\nLike a red-faced farmer.\nI did not stop to speak, but nodded,\nAnd round about were the wistful stars\nWith white faces like town children.",
                                "My life has been the poem I would have writ\nHenry David Thoreau\n\nMy life has been the poem I would have writ\nBut I could not both live and utter it.",
                                "Believe This\nWilhelmina Stitch\n\nYou’re winning. You simply cannot fail.\nThe only obstacle is doubt;\nThere’s not a hill you cannot scale\nOnce fear is put to rout.\nDon’t think defeat, don’t talk defeat,\nThe word will rob you of your strength.\n“I will succeed,” This phrase repeat\nThroughout the journey’s length.",
                                "The Shortest And Sweetest of Songs\nGeorge MacDonald\n\nCome\nHome.",
                                "The Duck Poem\nOgden Nash\n\nBehold the duck.\nIt does not cluck.\nA cluck it lacks.\nIt quacks.\nIt is specially fond\nOf a puddle or pond.\nWhen it dines or sups,\nIt bottoms ups.",
                                "147\nSappho, trans. Anne Carson\n\nsomeone will remember us\nI say\neven in another time",
                                "24A\nSappho, trans. Anne Carson\n\nyou will remember\nfor we in our youth\ndid these things\nyes many and beautiful things",
                                "The Old Pond\nMatsuo Bashō\n\nAn old silent pond\nA frog jumps into the pond—\nSplash! Silence again.",
                                "Lighting One Candle\nYosa Buson\n\nThe light of a candle\nIs transferred to another candle—\nSpring twilight",
                                "Spring Ocean\nYosa Buson\n\nSpring ocean\nSwaying gently\nAll day long.",
                                "The West Wind Whispered\nR.M. Hansard\n\nThe west wind whispered,\nAnd touched the eyelids of spring:\nHer eyes, Primroses."
                               };

 

]]>
Corrective Lens Tracker and Holder https://courses.ideate.cmu.edu/60-223/f2022/work/corrective-lens-tracker-and-holder/ Thu, 27 Oct 2022 18:39:21 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16660 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("        "));
}

 

 

]]>
Do Not Disturb Toaster https://courses.ideate.cmu.edu/60-223/f2022/work/do-not-disturb-toaster/ Thu, 27 Oct 2022 11:16:27 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16623 This device is intended to let everyone around you know when you are working or not so that you are able to stay on task.

This is the toaster switching from free time mode to work mode. You see once work mode is on, the lights starts to flash which is from the noise in the room. The linear actuator has to complete its’ movement before the mode properly switches.

Main Photo For Scale: The toaster has a “Do Not Disturb” sign and a “Free To Talk” sign at the top. 10 LEDs on the top. An LCD that says “Break Time!” or “Work Time!”. A switch to start timer and sound sensor are in the back.

Detail Shot: This gives a good view of the top so you can see the whole top and all the LEDs. Also in this photo you can see the button, sound sensor, and the hole for all the cords, which is located on the back of the toaster. When the button is clicked it starts the Pomodoro timer.

Detail Photo: This is the inside of the toaster. The wiring is pretty intense because of the 10 LEDs. I had to tape/puddy some connections so they wouldn’t come undone. I taped down/glued two bread boards inside so they stayed stable in the box to avoid loose connections, and so the switch would be available on the outside of the box.

Action Photo: When the “Free To Talk” sign is up that means it is break time according to the Pomodoro timer. The timer will constantly switch between 25 minutes and 5 minutes because it is based off the Pomodoro technique for most efficient work. Work time is suppose to be 25 minutes long and break time is 5 minutes long.

When the “Do Not Disturb” sign is up that means it is work time. The lights will flash if you start talking and if you talk loud enough they will blink red for 1 second to alert you that you are distracting the user from being productive. When the “Do Not Disturb” sign lowers then the toaster will stop flashing the LEDs.

Action Photo: There is an LCD screen that is for the user to look at so they can know if it’s break time or work time. It is also there as another reminder to stay focused.

Action Photo: The sound sensor does not send signals to the lights when the “Free To Talk” sign is up. If there is minimal noise the lights will not light up. The toaster needs to detect “disturbing levels of noise” to flash lights. This makes it so the toaster can be used in a public area and notify people when they are being too disturbing so that the user doesn’t need to do it themselves.

Process

The first big decision in my process was deciding how the sound detector was going to identify it’s signals. I originally organized my sounds into quiet, moderate, and loud. After Zach helped me get a more reliable sound detection, I started messing with the sensitivity. I was adjusting what the Arduino identified as quiet and loud, and at this point I made the decision on what the toaster is going to react too. I decided that first of all there was going to be some wiggle room so that a loud room does not trigger the toaster. Then I made the decision that moderate and loud would have different response. I decided here that the toaster should get “more angry” when it detects loud noise, which would cause it to flash the red light longer. I think this thinking and decision making really effected the logic of my code and how I approached it. This decision came pretty earlier on and I think that is made obvious by how I coded the whole device. It overall had a heavy influence on the design.

The second big decision came much later in the process. When I was deciding how the toaster was going to look, I also considered how it was going to act and be interacted with. I made a couple last minute decisions when designing the outer look and experience. There were two things that I felt like were feeling unclear in my design. First, I was concerned it wouldn’t be obvious enough when the toaster would detect loud sounds. This made me realize I am going to need an LED strip or lots of LEDs. I decided that the design would look kinda pretty and less tacky if used little LEDs and had them dotted around the top, plus it would be hard to miss many LEDs flashing at you, especially 10. Secondly, I realize for some it may not be fully clear when it is break time or work time, so that is when I decided the LED screen was necessary, because it wasn’t always planned to be in the design. Thus, when I was designing the lasercut box I put in a rectangle hole for the LCD, however there was then an even more last minute switch up. Last minute I realized I made a switch for my box and that needs to be easily accessible on the outside. So, I ended up using the LCD hole as a place to put the breadboard with the switch in it so I didn’t have to open the box to start it. In the picture you can see the lasercut box before it’s fully assembled, and at this moment is when I realized I need to move the switch off my big breadboard and onto a tiny one.

 

This is the first sketch of my device. What’s interesting is in the beginning of ideating I wasn’t thinking about the code as much, so in this drawing I have a knob that you can turn to the amount of time you want to work. I quickly realized after I moved out of ideating that this could be difficult and would potentially be a lot of coding, so I later opted for a button which is a simple on/off mechanism rather than the user being able to select a time.

This is my setup to test the sound detector to light signal. So this helped me visualize how sensitive the sound detector is and also if the LED would actually light up when I wanted too. I did a lot of clap and talking and yelling with this simple set up when trying to calibrate my sensor properly.

 

During the whole coding process I constantly had many Arduino files open. That is for a couple reasons. First reason is because I was looking at example code and trying new things out, so if I wanted to try a new approach I just made a new sketch and attempted in the new sketch (hence in this picture why I have two LCD sketches). Secondly, some sketch files were more for testing sensors or outputs. I had one sketch file that would reset the linear actuators for me. I also had a sketch file that would test the sound detector. I had another sketch file that would print the timer on the LCD screen for me. All of this helped me debug my code. Lastly, I was scared of messing up whatever I made work successfully, so I had each part of the system in it’s own sketch. Thus, I had an LCD sketch, Timer sketch, Sound Sensor + LED sketch, and a linear actuator sketch. So at the end of coding I had to compile all my sketches together carefully and neatly.

This is me wiring the button. I never wired a button before I had to kinda look it up and guess, but it ended up successfully and coded the button after wiring it.

I had a lot of trouble with making the linear actuator fit into the code properly with my timer code I made. The main reason is because linear actuators aren’t told to go a distance, they are just told to go and stop. The example code I referenced used delays to make the linear actuator go the distance it wanted, so it was really difficult to fit that into code that was all based around millis(). It took a lot of experimenting with the linear actuators just sat on the table until I was able to figure out the code. It was also difficult because I have a break time mode and a work time mode in my code, so at first I had no idea where which linear actuator should be extended or retracted. That took a little bit of guessing and thinking through.

Discussion

I am overall pretty happy with my project. I think it does exactly what I intended it to do. It detects sound levels decently well and the linear actuators actually work perfectly how I intended them too. I think if anything I wish I could add in more of my fun ideas. For example, I wish I could add more stimuli to happen when the noise levels get loud. I think that would make the project even more fun and interesting. I also wish I did put more time into the design of the project just because I am a design major and I think it would enhance the interaction of this device.

 

Overall, I did fumble a lot along the way. I think I still don’t fully understand the sparkfun sound detector. I tried to calibrate it exactly how I wanted, but I only had so much time and I never got back around to it. Yet, the hardest part was actually making the timer and switching the modes between work and break. I am used to coding in functions, so for me, it is hard to code into a loop. A couple times I forgot that the program is constantly looping, which caused bugs. It took me a while to figure out ways around it (aka I made variables that acted as switches), but it was hard to figure out how to make my initial logic work. Then after that I had to add in the linear actuator code which was challenging because I couldn’t remember where break mode and work mode was being turned off and on. So, I think overall, my code could’ve been much cleaner because it started to confuse me. I think there’s too many switches on and off and there must be a way to simplify and make the variable names clearer.

The critiques brought up some great points and I actually ended up listening to one of them. The first one that stood out said “Maybe add double sided signs and have the signs disappear all the way into the toaster”. I strongly agreed with them and I ended up remaking my signs to be double sided and sturdier, however, the linear actuators are too tall to actually get the signs all the way in, but I would’ve loved to also incorporate that into my design. The second critique said “I would say if, theoretically, you were trying to use this device in a library, or somewhere quiet, then the sound of the linear actuators might be a bit loud and counterintuitive for its purpose”. I honestly don’t have a solution for that currently, but I thought this was an interesting thing to point out. I kinda agree that the sound could end up being distracting, yet my limitations were partially due to what was available.

If I were to make a whole new iteration, I would definitely clean up the wiring and code, since those both caused me lots of extra time. However, I also think there’s so many ways to expand on this idea like adding a more advanced timer, or adding more interactions that help keep you on task, thus I think this would be a really good project to iterate on because of the endless possibilities.

Technical Information

/* The Do Not Disturb Toaster
  By: Gia Marino

  My code has a switch that tells when the program should start.
  Then the program takes a start time and starts calculating time passed.
  If currentTime hits 25 mins and the program is not in break mode
  then it will switch over to break mode and move the linear actuator to
  indicate this. If it is in break mode and the currentTime hits 5 minutes
  then the program will switch into break mode and moves linear actuator
  accordingly. If in workmode the program will take inputs from the sound
  detector and send signals to the LED pin if detects moderate or loud noise.
  It continuously switches inbetween these modes unless stopped.


  Arduino pin | role   | description
  ------------|--------|-------------
  A0            input    enevelope pin on sound detector
  2             output   gate pin on sound detector
  6             output   LED pin
  4             input    button pin
  7             output   DO NOT Disturb linear actuator
  8             output   DO NOT Disturb linear actuator
  11            output   FREE to talk linear actuator
  12            output   FREE to talk linear actuator
  SDA           output   LCD serial pin
  SCL           input    LCD clock pin
  GND           input    ground
  5V            output   5v

  My linear actuator code was inspired by
  https://www.firgelliauto.com/blogs/tutorials/how-do-you-control-a-linear-actuator-with-an-arduino.
  I used this code to start and ended up breaking it up into two parts and putting that into my if
  statements. Secondly, my sound detector code was copied from
  https://learn.adafruit.com/adafruit-microphone-amplifier-breakout/measuring-sound-levels. I basically
  left most of it the same since it worked well for my application and I labeled it as the sound detector code.
  Additionally, I used https://learn.sparkfun.com/tutorials/sound-detector-hookup-guide/all#software-example
  as a launchpad to creating my sound detector code. I ended up not following it for my main code, but my code
  may be slightly inspired by this source. To understand button wiring and coding, I referenced https://youtu.be/TIBa_RQB3Ek.
  Lastly, we copied the LCD tutorial code and worked off of that, so our code for the LCD display
  is also heavily influenced by this tutorial: https://courses.ideate.cmu.edu/60-223/f2022/tutorials/I2C-lcd
*/


//LCD set up
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);

#define BUTTON_PIN 4    //button pin
#define PIN_GATE_IN 2   //sound detector
#define PIN_LED_OUT 6   // all LEDs are hooked up to pin 6
#define PIN_ANALOG_IN A0    //sound detector

// sound detector variables
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;

bool workTimerSwitch; //tells the program it is work mode
bool breakTimerSwitch;  //tells the program it is break mode
//this tells program whether it is break time mode (when 1) or work time mode (when 0)
bool breakTimerON;

unsigned long startTime; // when the timer starts
unsigned long currentTime = 0; // how long the timer has been going

bool DEMO_MODE = 1; // Demo mode is turned on if 1
bool startToaster = 0;  // variable that indicates that button turned on toaster
bool TURN_MIC_ON = 0; // lets the program know whether the LEDs should flash if theres sound


void setup() {

  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
  pinMode(PIN_LED_OUT, OUTPUT);

  //LCD
  // initialize the screen (only need to do this once)
  screen.init();
  // turn on the backlight to start
  screen.backlight();
  screen.clear();

  //break time actuator
  pinMode(7, OUTPUT); // Configure pin 7 as an Output
  pinMode(8, OUTPUT); // Configure pin 8 as an Output

  digitalWrite(7, HIGH); // Initialize pin 7 as Low
  digitalWrite(8, HIGH); // Initialize pin 7 as Low

  //work time actuator
  pinMode(11, OUTPUT); // Configure pin 11 as an Output
  pinMode(12, OUTPUT); // Configure pin 12 as an Output

  digitalWrite(11, HIGH); // Initialize pin 11 as Low
  digitalWrite(12, HIGH); // Initialize pin 12 as Low


}

void loop() {

  unsigned long globalTime = millis(); // the global time

  //this variable stores whether button was pressed
  int ON = digitalRead(BUTTON_PIN);

  // if button is pressed then this if statement swtiches the startToaster
  //variable on HIGH which will start the program
  if (ON == 1) {
    startToaster = 1;
  }

  if (startToaster == 1) {
    currentTime = globalTime - startTime; // calculates currentTime
  }

  if (currentTime >= 1500000) { // work timer reached 25 mins
    Serial.println("25 mins!");

    // Retracts Linear Actuator
    digitalWrite(11, HIGH);
    digitalWrite(12, LOW);
    delay(2000); // 2 seconds
    // Stop Actuator
    digitalWrite(11, HIGH);
    digitalWrite(12, HIGH);

    // extends breaktime linear actuator
    digitalWrite(7, LOW);
    digitalWrite(8, HIGH);
    delay(2000); // 2 seconds
    digitalWrite(7, HIGH);
    digitalWrite(8, HIGH);

    // it is break mode so sound does not need to activate LED
    TURN_MIC_ON = 0;

    //LCD
    screen.home();
    screen.print(String ("Break Time!"));

    startTime = globalTime; // reset
    breakTimerSwitch = 1; // turns on break time mode
  }

  // this is so the program doesn't continue to loop in the workTimer statment
  // b/c otherwise it would continue to reset startTime which would make currentTime zero
  // hence why the variable is labeled as a switch
  if (workTimerSwitch == 1) {
    startTime = globalTime;
    workTimerSwitch = 0;
  }

  if (breakTimerSwitch == 1) { // break timer switched on
    breakTimerON = 1;
    startTime = globalTime; // reset
    breakTimerSwitch = 0; // switch is turned off so program doesn't loop in statement
  }

  if (breakTimerON == 1) {
    if (currentTime >= 300000) { // 5 mins
      Serial.println("5 mins!");

      // Retracts break time Linear Actuator
      digitalWrite(7, HIGH);
      digitalWrite(8, LOW);
      delay(2000); // 2 seconds

      // Stop Actuator
      digitalWrite(7, HIGH);
      digitalWrite(8, HIGH);

      // Extend Linear Actuator
      digitalWrite(11, LOW);
      digitalWrite(12, HIGH);

      delay(2000); // 2 seconds

      // Stops Actuator
      digitalWrite(11, HIGH);
      digitalWrite(12, HIGH);

      // sound detection should now activate LEDs since in work mode
      TURN_MIC_ON = 1;

      // LCD
      screen.home();
      screen.print(String ("Work Time!"));

      startTime = globalTime; // reset
      breakTimerON = 0; // will make program exit this if statement
      workTimerSwitch = 1; // switch into work mode
    }
  }

  // SOUND DETECTOR CODE
  if (TURN_MIC_ON == 1) {
    unsigned long startMillis = millis(); // Start of sample window
    unsigned int peakToPeak = 0;   // peak-to-peak level
    unsigned int signalMax = 0;
    unsigned int signalMin = 1024;

    // collect data for 50 mS
    while (millis() - startMillis < sampleWindow)
    {
      sample = analogRead(PIN_ANALOG_IN);
      if (sample < 1024)  // toss out spurious readings
      {
        if (sample > signalMax)
        {
          signalMax = sample;  // save just the max levels
        }
        else if (sample < signalMin)
        {
          signalMin = sample;  // save just the min levels
        }
      }
    }

    peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
    double volts = (peakToPeak * 5.0) / 1024;  // convert to volts

    if (volts <= 0.10) // quiet noise levels
    {
      Serial.println("Quiet.");
      digitalWrite(PIN_LED_OUT, 0);
    }
    else if ( (volts > 0.10) && ( volts <= 0.30) )
    {
      Serial.println("Moderate.");    // moderate noise levels
      digitalWrite(PIN_LED_OUT, 50);
    }
    else if (volts > 0.30)
    {
      Serial.println("Loud.");     // loud noise levels
      digitalWrite(PIN_LED_OUT, 200);
    }
  }

  // DEMO MODE

  if (DEMO_MODE == 1) { // DEMO MODE is turned on
    if (currentTime >= 6000) { // 1 min

      // Retracts Linear Actuator
      digitalWrite(11, HIGH);
      digitalWrite(12, LOW);
      delay(2000); // 2 seconds
      // Stop Actuator
      digitalWrite(11, HIGH);
      digitalWrite(12, HIGH);

      // extends breaktime linear actuator
      digitalWrite(7, LOW);
      digitalWrite(8, HIGH);
      delay(2000); // 2 seconds
      digitalWrite(7, HIGH);
      digitalWrite(8, HIGH);

      // it is break mode so sound does not need to activate LED
      TURN_MIC_ON = 0;

      //LCD
      screen.home();
      screen.print(String ("Break Time!"));

      startTime = globalTime; // reset
      breakTimerSwitch = 1; // turns on break time mode
    }
    if (breakTimerON == 1) {
      if (currentTime >= 6000) { // 1 min

        // Retracts break time Linear Actuator
        digitalWrite(7, HIGH);
        digitalWrite(8, LOW);
        delay(2000); // 2 seconds

        // Stop Actuator
        digitalWrite(7, HIGH);
        digitalWrite(8, HIGH);

        // Extend Linear Actuator
        digitalWrite(11, LOW);
        digitalWrite(12, HIGH);

        delay(2000); // 2 seconds

        // Stops Actuator
        digitalWrite(11, HIGH);
        digitalWrite(12, HIGH);

        // sound detection should now activate LEDs since in work mode
        TURN_MIC_ON = 1;

        // LCD
        screen.home();
        screen.print(String ("Work Time!"));

        startTime = globalTime; // reset
        breakTimerON = 0; // will make program exit this if statement
        workTimerSwitch = 1; // switch into work mode
      }
    }
  }
}

 

]]>
Light Signal For Bike https://courses.ideate.cmu.edu/60-223/f2022/work/light-signal-for-bike/ Thu, 27 Oct 2022 05:25:18 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16380 This device is designed to be placed on a bike, and signal to other vehicles with its lighting.

 

Overall Picture with Reference

Back View of the Device

Video of it Working:

This video is taken in the dark to show its real-world effect as such a device is most crucial during nighttime.

Process & Decisions:

The first major decision I made is to utilize multiple small pieces of LED strip and orientate them in a specific way to show a signal for left turn, right turn, and breaking. In my initial design, I have a circle as part of my stop signal. However, bending LED strips isn’t really a choice, and the only solution to that is to place the LEDs perpendicular to the surface of the device, which then makes them very hard to mount as those LED strips already have tapes applied on the back. The final decision on this is to remove the circular shape LED strip and have a rectangular-shaped stop sign instead, which still looks very good as shown in the video above.

Planned Layout In The Initial Design

LED Strips Layout on the Device

The second decision is about powering the whole system. LED strips take quite a bit of power, so I decided to use a battery pack with 4 rechargeable AA batteries to power both the Arduino, the LED strips, and all four switches. To organize all the power lines and prevent any possible shorts, I used a small circuit board to sort out all the wires. In addition, I added a small power switch that is embedded in the top wall of the case to control the power of the device.

An Overview of the Inside

Main Circuit Board

Another decision I made are the inputs. The left turn and right turn inputs are non-toggled regular buttons, which I believe to be most convenient in actual usage as they are intended to be placed rider’s two thumbs on the handlebar. Needless to say, the night light switch is a toggled switch and the break switch is a hinge lever switch that will be placed between the break bar and the handlebar, so whenever the rider breaks, the lever switch gets pressed and actuates as well.

Switches From Top to Bottom: Break, Night Light, Left Turn, Right Turn

Discussion

One major criticism of this device is the night light color. Conventionally, white lights are located at the front of a vehicle, so having a night light being white and placed at the rear of the bike may very likely confuse the person behind especially in the dark. However, since those LED strips are programmable in terms of their colors, I could easily change the color for a night light, which could be a potential solution. Another criticism is the case of the device. Being weatherproof is a crucial part of any device that is intended to be used outside, and this one is no exception. Yet the wooden case isn’t the best choice in terms of weatherproofing.

Such an issue leads to my view of this project. I consider this device still at a development stage, as it is not ready to be brought outside and use in the real world. My original intention is to mount this device to the back of my bike, but that goal is still quite far as the mounting brackets are still to be made. Overall, I think I have achieved everything I want for this device to work in a lab, but further development is needed for it to be actually useful.

Though I may not be all that satisfied with the device itself, what I was satisfied with are the things that I learned and experienced throughout this project. Things that I have never heard of like potting and experiences using the laser cutter for the wooden casing. I also learned from other people who utilized acrylic for their projects as that would be a better material for me to use instead of plywood as it is at least waterproof if sealed properly and allow for a clear view of the inside for presentations. After all, the biggest lesson that this project has given me is to think ahead to real-world applications and challenges when designing.

It is on my schedule to make further improvements to this project, namely using the smaller Arduino micro or nano and going through the process of potting for a more secure and waterproof case. As for the buttons, I will look into some waterproofed switches and buttons and ways to mount them onto a bike.

Technical Information

Functional Block Diagram

Electrical Schematic

/* Light Signal For Bike

    The following code is intended to lights up a set of LED strips according to the 4 inputs.
    When Left Turn is on, Upper Left, Lower Left, and Middle LED Strip turn "on", which is red
    When Right Turn is on, Upper Right, Lower Right, and Middle LED Strip turn "on", which is red
    When Break is on, All 5 LED strips turn "on", which is red
    When Night Light is on, All 5 LED strips turn "night", which is white
    Night Light has the lowest priority while other three action has equal priority.
    Pin Mapping:
    Arduino Pin  /  Role / Description
      2            Output   Upper Left LED Strip
      3            Output   Middle LED Strip
      4            Output   Upper Right LED Strip
      5            Output   Lower Right LED Strip
      6            Output   Lower Left LED Strip
      7            Input    Left Turn Signal
      8            Input    Right Turn Signal
      9            Input    Break Signal
      10           Input    Night Light Signal

      Ethan Hu, 2022
*/

#include <PololuLedStrip.h>
// setup PololuLedStrip and set each strip to a pin number
PololuLedStrip<2> ledStripLU;
PololuLedStrip<6> ledStripLD;
PololuLedStrip<4> ledStripRU;
PololuLedStrip<5> ledStripRD;
PololuLedStrip<3> ledStripM;
// defind the number of LED on each strip, the maximum LED count used in this project is 6
#define LED_COUNT 6
// define the input pin number for the four switches
#define Left_Input 7
#define Right_Input 8
#define Nightlight_Input 10
#define Break_Input 9
// create lists of colors for the LED strips to show under different circumstances
rgb_color on[LED_COUNT];
rgb_color off[LED_COUNT];
rgb_color night[LED_COUNT];

void setup() {
  pinMode(Left_Input, INPUT);
  pinMode(Right_Input, INPUT);
  pinMode(Nightlight_Input, INPUT);
  pinMode(Break_Input, INPUT);
  // set up the list of color
  for (int i = 0; i < LED_COUNT; i++) {
    // on is red
    on[i] = rgb_color(255, 0, 0);
  }
  for (int i = 0; i < LED_COUNT; i++) {
    // off is clear/nothing
    off[i] = rgb_color(0, 0, 0);
  }
  for (int i = 0; i < LED_COUNT; i++) {
    // night is white
    night[i] = rgb_color(255, 255, 255);
  }
}

void loop() {
  // if left turn
  if (digitalRead(Left_Input) == HIGH) {
    ledStripLU.write(on, LED_COUNT);
    ledStripLD.write(on, LED_COUNT);
    ledStripRU.write(off, LED_COUNT);
    ledStripRD.write(off, LED_COUNT);
    ledStripM.write(on, LED_COUNT);
  }
  // if right turn
  else if (digitalRead(Right_Input) == HIGH) {
    ledStripRU.write(on, LED_COUNT);
    ledStripRD.write(on, LED_COUNT);
    ledStripLU.write(off, LED_COUNT);
    ledStripLD.write(off, LED_COUNT);
    ledStripM.write(on, LED_COUNT);
  }
  // if break
  else if (digitalRead(Break_Input) == LOW) {
    ledStripRU.write(on, LED_COUNT);
    ledStripRD.write(on, LED_COUNT);
    ledStripLU.write(on, LED_COUNT);
    ledStripLD.write(on, LED_COUNT);
    ledStripM.write(on, LED_COUNT);
  }
  // the second else is used to check whether if night light is toggled
  else {
    // if night light is toggled on
    if (digitalRead(Nightlight_Input) == HIGH) {
      ledStripRU.write(night, LED_COUNT);
      ledStripRD.write(night, LED_COUNT);
      ledStripLU.write(night, LED_COUNT);
      ledStripLD.write(night, LED_COUNT);
      ledStripM.write(night, LED_COUNT);
    }
    // all the LED will turn off if no signal is sent to it and night light is not toggled on
    else {
      ledStripRU.write(off, LED_COUNT);
      ledStripRD.write(off, LED_COUNT);
      ledStripLU.write(off, LED_COUNT);
      ledStripLD.write(off, LED_COUNT);
      ledStripM.write(off, LED_COUNT);
    }
  }
}
]]>
Metronome https://courses.ideate.cmu.edu/60-223/f2022/work/metronome/ Thu, 27 Oct 2022 04:23:59 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16442 This metronome functions as a musician’s tool to easily control beats per minute, switch between time signatures, as well as adjust volume using buttons and potentiometers for a range of possibilities when practicing.

Close Up

On/Off Button, Volume Control, and LCD Displaying BPM and Time Signature

Speaker, BPM Control, Time Signature Button

Turning Potentiometer to Control BPM (beats per minute)

Pressing Button to Change Time Signature (4/4, 2/4, 6/8)

Switching Out Piezo Buzzer for Larger Speaker

Moving from Usual Metronome Shape to Horizontal Design

Process

Using Potentiometer to Control Volume by Changing Resistance

Code Used to Control Speaker Using Volume Library (Eventually Changed to mills())

Adding Potentiometer that Controls BPM

Discussion

This project, though the idea of a metronome seems simple, posed problems along the way that required me to rework hardware and software many times throughout the project timeline. The constant reworking, though, allowed me to develop a project that I am ultimately proud of and will be using to assist me in my piano practice sessions. The final critique, I think, reflected what I thought about the project myself – the sound was “easy on the ear,” the design allowed for easy understanding, and the BPM and volume additions were helpful in making it more personalized than the metronome on my digital keyboard. However, I do agree with constructive comments I received – that I could add more time signatures (or even have the ability to input a desired one) and add phrases like “adagio” (slow tempo), though I believe the latter would be more complicated, as “adagio” has a range of BPM from 66 – 76, rather than a specific one. Ultimately, any simple idea has a world of possibilities to explore, and after critique, I will be adding some of their ideas as having more options becomes helpful. And though I am happy with the way it turned out, with the knowledge that I now have about coding after completing this project, I could make some small changes that would vastly improve the usability of the metronome. For example, the BPM was not reflected correctly on the LCD display and the piece didn’t fully close due to the breadboard and Arduino, but by editing the code to reflect the actual BPM and not the wait value, and using a solder breadboard and a smaller Arduino, I can easily get the interior hardware to fit inside the case I designed and have the LCD reflect exactly what I want. This project really opened my eyes to all the different ways you can code or set up something to have the exact same result. For example, my potentiometer that controlled volume needed to be set up differently from the usual VCC, GND, and output pins and instead was connected to ground through the piezo buzzer. I also had the same realization while coding when I was able to produce noise through a speaker, but in order to create time signatures, I had to scrap all of my code and begin again using millis() to make sure everything worked together. This was a recurring theme in my project – reworking – but, in the end, it came together and, with a bit of editing code and soldering, will be a fixture on my keyboard that I will continue to use and develop beyond the end of this class.

Functional Block Diagram and Schematic

Code

/* Metronome
 *  
 * The following code works to create a metronome that controls BPM (beats per minute), volume, and time signature.
 * The time signature and BPM are displayed on an LCD Display.
 * The time signature begins at 2/4. When the time signature button is pressed, time signature becomes 4/4.
 * When pressed a third time, time signature becomes 6/8. When pressed again, the time signature will 
 * become 2/4 and repeat again when button is pressed.
 * 
 * Pin Mapping:
 * 
 * Arduino Pin /  Role   / Description
 *      A1        INPUT     Controls BPM     
 *      2         INPUT     Controls time signature
 *      6         INPUT     Turns metronome on or off
 *      11        OUTPUT    Controls speaker
 *      
 * Francesca Menendez, 2022 */


#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const int SPEAKERPIN = 11; /
const int TIMESIGPIN = 2;
const int ONOFFPIN = 6;
int WAITPIN = A1;
int wait = 500; /* Gap between beats */
unsigned long timeGoal = 0;
unsigned long lastTime;
unsigned long counter = 0;
int state = 0; /* State begins at 0 */
bool on = false;
int potVal = 0;
int frequency = 500;
LiquidCrystal_I2C screen(0x27, 16, 2);

void setup() {
/* Sets Up Pins Used */
  pinMode(SPEAKERPIN, OUTPUT);
  pinMode(ONOFFPIN, INPUT);
  pinMode(WAITPIN, INPUT);
  pinMode(TIMESIGPIN, INPUT);

/* Setup for LCD Display */
  Wire.begin();
  screen.init();
  screen.backlight();
  screen.home();

  Serial.begin(9600);
}

void loop() {
  /* Creates timer using millis() to then set up time signatures */
  int timer = millis();
  wait = map(wait, 0, 1023, 10, 2000);
  wait = analogRead(WAITPIN);

  if(digitalRead(ONOFFPIN)== HIGH) {

/* When time signature button is pressed, state increases */
    if (digitalRead(TIMSIGPIN) == HIGH) {
      state++;
      delay(100);
    }

/* Metronome begins at 2/4 time signature */
    if (state%3 == 0) {
      if (counter % 2 == 0) {
        frequency = 800;
      }
      else {
        frequency = 500;
      }
    }
/* When time signature button is pressed, time signature becomes 4/4 */
    if (state%3 == 1) {
      if (counter % 4 == 0) {
        frequency = 800;
      }
      else {
        frequency = 500;
      }
    }
/* When time signature is pressed a third time, time signature becomes 6/8 */
    if (state%3 == 2) {
      if (counter % 6 == 0) {
        frequency = 800;
      }
      else if (counter % 3 == 0) {
        frequency = 400;
      }
      else {
        frequency = 500;
      }
    }

  if (millis() - lastTime >= wait) {
    tone(SPEAKERPIN, frequency, 100);
    counter++;
    lastTime = millis();
  }
  
  /* LCD updates every half of a second */
  if(millis()%250 == 0 ) {
    screen.clear();
  }
 
/* Prints BPM and Time Signature to LCD Display */
  if(1000 < millis() - timer) { 
      screen.setCursor(0, 0);
      screen.print("BPM:");
      screen.setCursor(4,0);
      screen.print(wait);
      screen.setCursor(0, 1);
      if(state%3 == 0) {
        screen.print("Time Sig: 2/4");
      }
      if(state%3 == 1) {
        screen.print("Time Sig: 4/4");
      }
      if(state%3 == 2) {
        screen.print("Time Sig: 6/8");
      }
      timer = millis();
    }
  }
}
]]>
Date Calc https://courses.ideate.cmu.edu/60-223/f2022/work/date-calc/ Thu, 27 Oct 2022 04:17:47 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16507 Calculates the average time it takes to use a limited item, like cereal, so I know when I’m going to run out/when to replace it.

(The video is too long to be uploaded as 1 piece, so I split it into three pieces.)

Initial view averages.

Add today’s date.

View updated averages.

Overall photo for proportion and scale.

The circuitry, from left to right: Arduino, SD card module, RTC (Real Time Clock) DS1307, rotary encoder.

View average amount of time between entries (AKA how long it takes to deplete/use that thing).

Add date entry to be used in future averaging calculations (first line gives the Unix time).

Process images and review

Original idea of being able to select an item to view/add date to (being able to keep track of multiple items at a time).

At first, I wanted to be able to save multiple categories of items and select which one I wanted to add/view dates from. I planned to have each row represent an item, with columns representing the data entries. However, there wasn’t a good way for the SD card reader to be able to parse to a specific point in the csv file to insert data: it only appends at the end, which may not be the correct place in the grid for the data. So unfortunately, I reduced it to one item, and thought that perhaps I should just switch out the SD Card if I wanted a different item logged. In hindsight, perhaps I could’ve tried creating a different file in the same SD card and selecting between those, or Zach said that apparently there is a way for the Arduino to remember values after turning off/on.

Storing dates in Unix (seconds since Jan 1, 1970).

I initially planned to store values as MM/DD/YYYY format in the csv for user readability if I look at the csv file directly. At first I thought storing/retrieving data was going to be straightforward and that I was going to be able to convert between data types. But that would also mean more parsing on the Arduino end because I’d have to separate all the values and then pack them together for storage, which is inconvenient. Furthermore, Arduino wouldn’t cooperate with converting between String/int types properly. So now everything is stored as Unix (seconds since Jan 1, 1970), so it’s just one number. I don’t need to do much parsing, and it’s easier to subtract without worrying about the lengths of months…etc.

Rotary encoder interface.

I decided to use a rotary encoder for a few reasons. First, I only need to move in 2 directions in a list of options, which can be coded in the direction you turn the knob. A joystick, for example, would have too many directions of movement that I’d have to reduce to 2 directions anyways. In contrast to a potentiometer, the rotary encoder allows you to keep turning in the same direction for as long as you’d like. Also, the rotary encoder also comes with a built-in button, so the option selection aspect is also taken care of. If I had used multiple buttons to move around and select, that would be more moving parts, which could mean more sources of error, and make things unnecessarily complicated.

I also learned how to make a nice interface for the knob by measuring the diameter with a caliper and designing those dimensions in Fusion. I hadn’t really thought about designing a surface to hide all the circuitry before, but it does seem like a good idea for future projects to both protect wires from damage and to make it look cleaner. If I had more time, I would’ve made an interface for the LCD as well. While construction paper wasn’t the most sturdy, it was more durable than I thought, probably because of all the solid parts underneath.

Make index selector loop around so you don’t just get stuck at the end of the list.

I wanted the knob selection to loop back around when it reached the end of the list, because that would be convenient instead of needing to scroll all the way up again, so I used some modular arithmetic to “loop” to the beginning of the list of options. Some shenanigans came up where the pointer of the selected item disappeared from the entire list completely, and apparently this was an Arduino thing, so adding +arrLen (the length of the list) to the equation apparently fixes things, even though it is mathematically the same as before. Arduino math is slightly different sometimes. 😐

1 tick felt by human is 4 ticks as measured by rotary encoder.

At first, I thought that each tick of the rotary encoder would have a difference of 1. So I used that raw difference to calculate the index of the list. However, I noticed that although I felt 1 “tick”, the difference on the Serial monitor was between 2 to 4. This was strange. After running some tests with the example code, I realized that each tick is actually a difference of 4, and the rotary encoder was reading quickly enough to register half a tick, which isn’t what I wanted. Thus, I wrote code to make sure the difference was a multiple of 4, so that it would “wait” until I was done turning a full “tick” before calculating the index.

Make sure only 1 button press is measured per time.

As with the rotary encoder, I know the Arduino measures things very quickly, but I only want it to register 1 button press at a time, instead of seeing it “pressed” multiple times when it was just one long press. So I had a global variable to help it remember whether it was in the middle of being pressed, and to only register the press once the button was released (completed button press).

Nice easy logic to print out instead of having a bunch of different String options.

I think my method of using the > as indicator of what’s selected was simple and effective, since you can still see all the other options in the list at the same time. In addition, instead of creating a bunch of varying Strings just to only use 1 of them (since the > location varies), I can just build one String. This is done 1 option at a time, and I insert the > in the correct place within the concatenation loop, so the logic is clean and simple. I also only cleared the LCD screen whenever the options changed in order to eliminate the “fuzziness” of the screen from clearing too rapidly.

Discussion

Some of the critiques were similar to ideas I already had, such as “Add a functionality to track the average use for multiple items”, which I was my original plan if I had learned some alternative ways of getting around the SD reading/writing challenges earlier, such as storing data directly in Arduino, or using multiple csv files. For the “multiple csv files” idea however, I could have a list of the titles of each file and use the index selection to pick a file to open. That would mean hardcoding the options, which is fine because if I wanted to add a new item to track, I’d probably want to set up the csv file properly via computer instead of through the Arduino, and then it wouldn’t be much harder to change the list to accommodate the new name while I’m at it. At first, I was against hardcoding options because it didn’t seem very flexible, but I realized there wouldn’t be too many changes needed, and could make the coding logic easier later.

A critique I hadn’t really thought about was having “a warning system based on the previous statistics”, such as a message that pops up before going to the Add/View options. I was just planning on using the date tracker in a passive way, by only consulting the data when I was curious. However, I think having a message pop up would be helpful in case I forgot something was going to run out sooner than I thought.

I had to learn 2 modules for this: the RTC and SD card, as well discover some more subtle things about the rotary encoder tick measuring as described earlier. The RTC was much simpler to understand since the library documentation was well-written with comments, and also because there were less options for what kind of functionality is needed for my project: I just need to know the current date, and don’t care about using it to set alarms…etc. Unfortunately, the SD card was much harder. First, there was no one library that was the top result, but a combination of SD and SPI stuff. The example code was usually for txt files. The ones for csv just logged data as a single column, but I wanted to add a value to this line of the csv, not the other lines. Searching up the question gave forum results that were similar, but not quite the same as what I wanted. We got so close as to be able to put the “writing head” in the middle of the file and overwrite 1 character, but when I tried to replicate that with where the data was actually going to be, it went back to appending things to a newline at the end of the file. So I tried for many days to get the SD read/write to work with the csv format I wanted, but in the end I decided it would probably be easier to keep the csv file simpler so it would definitely work with example/suggested code, instead of trying to get it to work with something that even online forums didn’t give a straight answer to.

I think I did a good job of breaking the project down into different tasks, such as focusing on each module working separately before combining the code. I also worked around a lot of challenges that I hadn’t thought of, such as all of these SD card writing issues, by reducing the complexity of what was needed of the module (people said “The software complexity is a very admirable task to have taken on” and “I think it would have been very difficult to manipulate the sd card and keep track of the time and number, so nice job :)”), although it would’ve been nice to have known about those potential alternative solutions earlier. I was able to achieve the main goal of the project, which is to add and average dates. So it shouldn’t be too hard to scale this up to add more functionality, such as multiple items, using the ideas I have described above. If I have time, I may also try the technique of storing data on the Arduino instead of the SD card, although I’m not sure how much reworking of the code that would take, vs my hypothetical technique of keeping most of the code and SD card but adding a new file for each item, and scrolling through the list of (hard-coded) items to determine which time to view/add date.

Technical Information

Schematic.

/*
Date Calc
Freda Su
Goal: calculate running average of how long it takes to use an item ex: cereal
Whenever you run out/start cereal box, add date.
Check the average time it takes to use cereal so know when to replace.
Add date vs view time selected with rotating knob/button.

https://create.arduino.cc/projecthub/electropeak/sd-card-module-with-arduino-how-to-read-write-data-37f390
https://forum.arduino.cc/t/a-simple-function-for-reading-csv-text-files/328608
http://www.pjrc.com/teensy/td_libs_Encoder.html
https://github.com/cvmanjoo/RTC
https://github.com/GyverLibs/UnixTime/blob/main/examples/test/test.ino
https://courses.ideate.cmu.edu/60-223/f2022/tutorials/I2C-lcd

 Arduino pin | role   | description
 ------------|--------|-------------
 2             input    rotary encoder pin A
 3             input    rotary encoder pin B
 4             input    rotary encoder pin SWITCH
 10            output   CS pin
 11            output   MOSI
 12            input    MISO
 13            output   SCK
 SDA           both     LCD serial pin, RTC serial pin
 SCL           output   LCD clock pin, RTC clock pin
 GND           input    ground
 5V/VCC        output   5V
*/

#include <Encoder.h>
#include <Wire.h>
#include <I2C_RTC.h>
#include <UnixTime.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>
#include <SPI.h>
#define CS_PIN 10
//------------------------------------------------------------------------------
#define errorHalt(msg) {Serial.println(F(msg)); while(1);}  //something goes wrong w sd card
//------------------------------------------------------------------------------
File file;

const int SWITCH = 4;

long oldPosition  = -999;
bool flag = false; //not in the middle of being pressed
unsigned int index = 0; //menu option index
unsigned int oldIndex = 3;  //helps remember if rotated knob, dummy value, will change later
const int optArrLen = 2;  //length of the menu options array
unsigned int option = 0;  //menu option chosen

/* Create an LCD display object called "screen" with I2C address 0x27
  which is 20 columns wide and 4 rows tall. You can use any name you'd like. */
LiquidCrystal_I2C screen(0x27, 20, 4);

UnixTime stamp(-4);  //specify GMT
//not sure if this accounts for daylight savings??

static DS1307 RTC;

Encoder myEnc(2, 3);  //pins 2, 3 have interrupt capability


///////////////////////////////////// SET UP ///////////////////////////////////////////////////////
void setup() {
  Serial.begin(9600);
  pinMode(SWITCH, INPUT);
  if (!SD.begin(CS_PIN)) errorHalt("begin failed");

  // Create or open the file.
  file = SD.open("data.csv", FILE_READ);
  if (!file) errorHalt("open failed");
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
  RTC.begin();
  //RTC.setDateTime(__DATE__, __TIME__);  //only need to do once
  //RTC.startClock();
  RTC.setHourMode(CLOCK_H12);

  Serial.println();
  Serial.println("*** RTC 1307 ***");
  Serial.print("Is Clock Running : ");
  if (RTC.isRunning())
    Serial.println("Yes");
  else
    Serial.println("No. Time may be Inaccurate");
  Serial.println("Month-Day-Year");

  screen.init();

  // turn on the backlight to start
  screen.backlight();

  // set cursor to home position, i.e. the upper left corner
  screen.home();
  screen.clear();
  screen.print("righttext");
}

/////////////////////////////VOID LOOP////////////////////////////////////////////////////////////////////////

void loop() {
  //Screen 0: Log or check items?
  printOptions(index);
  while (!buttonPressDone())
  {
    index = chooseIndex(optArrLen, index);
    if (index != oldIndex)
    {
      screen.clear();
      printOptions(index);
      oldIndex = index;
    }
  }
  option = index; //index 0 = add date, 1 = check avg dates

  if (option == 0)  //added date for chooseItem = write to csv
  {
    unsigned long data = (unsigned long)(RTC.getEpoch());
    writeCSV(data);
    screen.clear();
    screen.home();
    screen.print("Writing ");
    screen.print(data);
    screen.setCursor(0,1);
    screen.print("Month: ");
    screen.print(RTC.getMonth());
    screen.setCursor(0, 2);
    screen.print("Day: ");
    screen.print(RTC.getDay());
    screen.setCursor(0, 3);
    screen.print("Year: ");
    screen.print(RTC.getYear());
    Serial.println(String(data));
    delay(5000);
    screen.clear();
  }
  else  //view dates = read line of csv, calculate average
  {
    float res = readCSV();
    screen.clear();
    screen.home();
    screen.print("Avg sec: ");
    screen.print(res);
    screen.setCursor(0, 1);
    screen.print("Avg min: ");
    res /= 60;
    screen.print(res/60);
    screen.setCursor(0, 2);
    screen.print("Avg hr: ");
    res /= 60;
    screen.print(res);
    screen.setCursor(0, 3);
    screen.print("Avg days: ");
    res /= 24;
    screen.print(res);
    delay(5000);
    screen.clear();
  }
}

///////////////////////////HELPER FUNCTIONS////////////////////////////////////////////////////////////////////////

//helps read each field of the csv
size_t readField(File* file, char* str, size_t size, char* delim) {
  char ch;
  size_t n = 0;
  while ((n + 1) < size && file->read(&ch, 1) == 1) {
    // Delete CR.
    if (ch == '\r') {
      continue;
    }
    str[n++] = ch;
    if (strchr(delim, ch)) {
        break;
    }
  }
  str[n] = '\0';
  return n;
}

//write date in unix (data) to csv
void writeCSV(unsigned long data)
{
  file = SD.open("data.csv", FILE_WRITE);
  file.println(data);
  file.close(); //save changes
}

//read dates for csv and calculate running average
float readCSV()
{
  file = SD.open("data.csv", FILE_READ);
  file.seek(0);
  float first = 0;
  float second = 0;
  float avg = 0;
  float numItems = 0;
  String temp = "";

  size_t n;      // Length of returned field with delimiter.
  char str[20];  // Must hold longest field with delimiter and zero byte.
  
  // Read the file and print fields.
  
  while (true) {
    n = readField(&file, str, sizeof(str), ",\n");
    // done if Error or at EOF.
    if (n == 0) break;

    // Print the type of delimiter.
    if (str[n-1] == ',') {
      str[n-1] = 0;
      Serial.print(str);
      Serial.print(",");
      
      // Remove the delimiter.
    }
    else if (str[n-1] == '\n')
    {
      str[n-1] = 0;
      Serial.println(str);
      if (first == 0)
      {
        temp = (String)(str);
        first = temp.toFloat();
      }
      else if (second == 0)
      {
        temp = (String)(str);
        second = temp.toFloat();
        numItems += 1.0;
        avg += abs(second - first);
      }
      else
      {
        avg += abs(second - first);
        second = first;
        temp = (String)(str);
        first = temp.toFloat();
        numItems += 1.0;
      }
    }
    else {
      // At eof, too long, or read error.  Too long is error.
      Serial.print(file.available() ? F("error: ") : F("eof:   "));
    }
    // Print the field.
  }
  file.close();

  return avg;

}

//want to only register press when "released" so doesn't pick up multiple presses for 1
bool buttonPressDone()
{
  if (digitalRead(SWITCH) == LOW)
  {
    flag = 1; //in the middle of being pressed...
  }
  else
  {
    if (flag) //was being pressed, but not anymore = full press
    {
      flag = 0; //reset
      return true;
    }
  }
  return false;
}

//use knob (rotary encoder) to select an option from menu (array)
unsigned int chooseIndex(unsigned int arrLen, unsigned int index)
{
  long newPosition = myEnc.read();

  //now want to have it cycle thru "array"
  //clockwise = decreasing value, CCW = increase
  if (newPosition != oldPosition)  //CCW
  {
    if ((newPosition - oldPosition) % 4 == 0) //want to measure whole ticks, dont measure too fast
    {
      //index movement calc
      long numTicks = (oldPosition - newPosition)/4; //each tick is 4 apart apparently?
      index = (index + numTicks + arrLen) % arrLen;
      oldPosition = newPosition;
    }
  }
  return index;
}

//used in early stages to print date to serial monitor to check if work properly
void printDate()
{
  switch (RTC.getWeek())
  {
    case 1:
      Serial.print("SUN");
      break;
    case 2:
      Serial.print("MON");
      break;
    case 3:
      Serial.print("TUE");
      break;
    case 4:
      Serial.print("WED");
      break;
    case 5:
      Serial.print("THU");
      break;
    case 6:
      Serial.print("FRI");
      break;
    case 7:
      Serial.print("SAT");
      break;
  }
  Serial.print(" ");
  Serial.print(RTC.getMonth());
  Serial.print("-");
  Serial.print(RTC.getDay());
  Serial.print("-");
  Serial.print(RTC.getYear());

  Serial.print(" ");

  Serial.print(RTC.getHours());
  Serial.print(":");
  Serial.print(RTC.getMinutes());
  Serial.print(":");
  Serial.print(RTC.getSeconds());
  if (RTC.getHourMode() == CLOCK_H12)
  {
    switch (RTC.getMeridiem())
    {
      case HOUR_AM :
        Serial.print(" AM");
        break;
      case HOUR_PM :
        Serial.print(" PM");
        break;
    }
  }
  Serial.print("Epoch: ");
  Serial.println(RTC.getEpoch());
  Serial.println();
  Serial.print("Epoch convert back: ");
  stamp.getDateTime(RTC.getEpoch());
  Serial.println(stamp.year);
  Serial.println(stamp.month);
  Serial.println(stamp.day);

  screen.clear();
  screen.autoscroll();
  // therefore, set the cursor one square off of right side of the screen
  screen.setCursor(16, 0);
  screen.print("righttext");
  screen.noAutoscroll();

}

//print menu options with arrow so easy to know which one it's on
void printOptions(unsigned int index)
{
  screen.home();
  String options[] = {"Add date", "View"};
  for (int i = 0; i < 2; i++)
    {
      String toPrint = "";
      if (i == index)
      {
        toPrint += ">";
      }
      toPrint += String(options[i]);
      screen.setCursor(0, i); //col 0, row i
      screen.print(toPrint);
    }
}
]]>
Carpel Tunnel Prevention Sleeve https://courses.ideate.cmu.edu/60-223/f2022/work/16342-2/ Thu, 27 Oct 2022 03:42:36 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16342 Introducing the Carpel Tunnel Prevention Sleeve, an innovative tool that buzzes the user into “Carpel Tunnel Syndrome”-safe arm positions.

Isometric View

Top View

Bottom View

Process Review

Major Decision 1:

The first major decision of this project was abandoning the Arduino Uno in favor of the Arduino Pro Micro. I wanted the Carpel Tunnel Prevention Sleeve to be a compact device, and with the dimension Arduino Uno I could not accomplish this goal. In addition, the Arduino Pro Micro made for much easier prototyping as the microcontroller could be soldered directly onto the circuit board.

Major Decision 2:

The second major decision of this project was changing the design of the Carpel Tunnel Prevention Sleeve to be more like an arm brace as opposed to the original bracelet idea. This change was a result of me struggling to keep both the VL53L0X Laser Distance Sensor and the ADXL335 3-axis Accelerometer parallel to the surface it was above. With the bracelet design, the sensors shifted around too much. With the new arm brace design, there was another VELCRO strip keeping the sensors in-place.

Process Images:

3-axis Accelerometer ADXL335 and VL53L0X Laser Distance Sensor soldered on a circuit board

Both vibrating motors soldered alongside an Arduino Pro Micro

The Carpal Tunnel Prevention VELCRO arm brace

Discussion

From the in-class critique, I received valuable feedback from my classmates that I would consider in the next iteration of the Carpel Tunnel Prevention Sleeve. One specific comment read: “Maybe figure out what’s up with the smell”. This is an incredibly important piece of feedback, because the smell the commenter is referring to the burning smell from the Arduino Pro Micro. My original plan was to power the Carpel Tunnel Prevention Sleeve using the four rechargeable AA batteries I got from IDEATE Lending. Unfortunately, all four batteries were dead and I was not given the charger. I ended up using a 9V battery for a device that can take up to 3.3V, which was not the best decision on my part.

In addition to this comment, the rest of the other comments talked to visual aspect of the Carpel Tunnel Prevention Sleeve. One such comment read: “I think it would be more usable if it was more comfortable and the wires were more hidden. More like a bracelet type of thing“. I agree with the concerns of this commenter. The Carpel Tunnel Prevention Sleeve is quite fragile and putting on and taking off the device is somewhat difficult. I wished I could have devised a clever way to hide the wires from the both the user and the audience.

On a scale from 1 to 5, where 1 is not satisfied at all and 5 is extremely satisfied, I am at a 4. I believe I was able to capture the functionality I was imagining during the Ideation Stage – a wearable device that buzzes. However, much of the visual design was not what I imagined. As of now, the design for the Carpel Tunnel Prevention Sleeve is a bit sloppy, and not sleek at all. I wish I could have improved the overall wiring of the device as some wires ended up crossing each other. I wish could have improved the soldering on the device as it isn’t too clean throughout the circuit board. Improving these three items would make me extremely satisfied with the Carpel Tunnel Prevention Sleeve.

A key discovery I made on this project was how difficult it is to make a compact device. Soldering, wiring, and designing in such as small scope was incredibly frustrating because there is little to no margin of error. Much of my struggle was a result of poor planning. If I could go back and talk to my past self, I would force him to spend a lot more time on planning out how each component will mesh with each other. On a more happy note, this project made me realize how much fun it was to play with VELCRO. In future project, I will look to include VELCRO at any step possible.

If I had to chance to build another iteration of the Carpel Tunnel Prevention Sleeve. I would investigate eliminating the wires completely. A bluetooth chip from IDEATE Lending maybe a good way to fulfill this desire. Another focus in the next iteration would be to make the arm brace a bit more fashionable. Perhaps, I could investigate making a full-on sleeve with fabric.

Block Diagram

Electrical Schematic

Code

/**
 * @Project Title: Carpal Tunnel Prevention Sleeve
 * @Author: Ethan Lu <ethanl2@andrew.cmu.edu>
 * @Description: 
 * Processes analog readings from a VL53L0X distance Laser Sensor and a ADXL335 analog accelerometer 
 * to calculate distance and angle relative to the table. If the distance and angle results in a
 * "bad position", the two vibrating motors will activate, signaling to the user to change positions.
 *
 * @Mapping:
 * Arduino Pin |  Role | Description
 * ------------|-------|------------
 *      2        INPUT    VL53L0X SDA
 *      3        INPUT    VL53L0X SCL
 *      4        INPUT   ADXL335 XOUT
 *      5        INPUT   ADXL335 YOUT
 *      6        INPUT   ADXL335 ZOUT
 *      8       OUTPUT   Motor OUTPUT
 *      9       OUTPUT   Motor OUTPUT
 */


/**
 * @brief: import header files
 */
#include <VL53L0X.h>
#include <Wire.h>
#include <math.h>

/**
 * @brief: define macros
 */
#define pinX 4
#define pinY 5
#define pinZ 6

#define pinMotorRight 8
#define pinMotorLeft 9

#define adcRef 3.3
#define zeroRef 1.569
#define sensitivityRef 0.3

/**
 * @brief: declare a VL53L0X object
 */
VL53L0X laser_distance;

/**
 * @brief: initialize variables
 */
unsigned x, y, z;
float vecX, vecY, vecZ;
float roll, pitch, yaw;

/**
 * @brief: initialize pinouts
 */
void setup() {
  Wire.begin();

  pinMode(pinX, INPUT);
  pinMode(pinY, INPUT);
  pinMode(pinZ, INPUT);

  pinMode(pinMotorRight, OUTPUT);
  pinMode(pinMotorLeft, OUTPUT);
  
  /**
   * @brief: initialize the VL53L0X object
   */
  laser_distance.init();
  laser_distance.setTimeout(500);
  laser_distance.startContinuous();

  analogReference(DEFAULT);
}

/**
 * @brief: activate the Carpal Tunnel Prevention Sleeve
 */
void loop() {
  int distance = laser_distance.readRangeContinuousMillimeters();
  delay(100);

  x = analogRead(pinX);
  y = analogRead(pinY);
  z = analogRead(pinZ);
  delay(100);

  vecX = vectorize(x);
  vecY = vectorize(y);
  vecZ = vectorize(z);
  delay(100);

  roll = eulerize(vecY, vecZ);
  pitch = eulerize(vecX, vecZ);
  yaw = eulerize(vecY, vecX);
  delay(100);

  roll = fabs(roll);
  pitch = fabs(pitch);
  yaw = fabs(yaw);

  /**
   * @brief: determine if a "bad position"
   */
  if(distance < 55) {
    /*
     * @brief: check if current position is bad
     */
    if(roll < 15.0 || 32.5 < roll || pitch < 19.5 || 32.5 < pitch) {
      activate();
      delay(1000);
    }
  }
  deactivate();
}

/**
 * @brief: determine the vector representation of an acceleration value
 * @param[in] a: acceleration value
 * @return: the predicted vector representation
 */
float vectorize(unsigned a) {
  return (a / 1024.0 * adcRef - zeroRef) / sensitivityRef;
}

/**
 * @brief: calculate the angle between two vectors
 * @param[in] v1: the first vector
 * @param[in] v2: the second vector
 * @return: the predicted angle between v1 and v2
 */
float eulerize(float v1, float v2) {
  return atan2(-v1, -v2) * (180 / PI) + 180;
}

/**
 * @brief: floating point equivalent to abs()
 * @param[in] x: the value to convert
 * @return: |x|
 */
float fabs(float x) {
  if(x < 0) {
    return -1 * x;
  }
  return x;
}

/**
 * @brief: send a 5V signal to the vibrating motors
 */
float activate() {
  digitalWrite(pinMotorRight, HIGH);
  digitalWrite(pinMotorLeft, HIGH);
}

/**
 * @brief: send a 0V signal to the vibrating motors
 */
float deactivate() {
  digitalWrite(pinMotorRight, LOW);
  digitalWrite(pinMotorLeft, LOW);
}

 

 

]]>
MINI Controller https://courses.ideate.cmu.edu/60-223/f2022/work/mini-controller/ Thu, 27 Oct 2022 03:14:41 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16528

By Harry Schneider

Description:

The MINI controller is a compact and portable MIDI controller which allows you to produce music more efficiently.

Discussion:

Overall, I am very happy with how this project turned out. I am very pleased with the build quality despite it being my first time 3D printing. Also I think the size and overall durability is great. My goal was to make something that, if manufactured, I would personally purchase and use. Maybe I am just overly critical but I would have liked to go smaller if possible. The current size is 3 x 3 inches but I would love if it could fit comfortably in my pocket. The current size was already a challenge for me. It was very hard to fit all of the electronics inside especially with the usb input attachment. This attachment came with a large cable that took up a lot of space on the inside. In the future I would like to make another version which would have a USB type C input instead of type B. Type C is much smaller and more compatible with current technology. Also I would make it around 2 inches by 2 inches instead of 3 inches. In addition I would also make it slimmer. I found the 3D printing process to be really fun. It was very difficult designing everything in cad but with the help of my teacher I was able to actualize the case for the midi controller.
I was very happy with the feedback from my critique. However I was told to consider labeling the functions of the potentiometers. I understand how this makes sense but in reality, the assignments of the knobs change depending on how I intend to use them. Also it was suggested that I could use a micro Arduino if I wanted to go smaller. I will definitely use a smaller Arduino in the future. I think this was really great feedback.

 

Photos and Videos

Process

This is the initial working prototype. At this point the code was working properly and all of the components where working on the breadboard. At this point I was designing the case where I needed to fit these components and I had to measure everything in order to create an accurate 3D model.

3D Modeling

After assembling the prototype, I created the case in cad, a 2 piece design with a bottom plate with screw holes as well as a 3D printed shell.

These are the electronics soldered together in a way to maximize internal space

This image shows how everything fits together in the 3D printed case. This part was a challenge and I was worried about everything fitting and at a point I was considering remaking the 3d model by making it bigger but I really wanted to keep it as small as possible. I decided to re-solder everything in a way that I could fit everything. I decided to use a flexible protoboard which I cut down in size to maximize as much space as possible

Schematic

Schematic

Block Diagram

Functional block diagram

/*
 Harry Schneider: MINI Controller
 Adapted from code for midi potentiometers from Gustavo Silviera Aka "Nerd musician" on youtube (http://www.youtube.com/musiconerd)
*/

#define ATMEGA32U4 1
#ifdef ATMEGA328
#include <MIDI.h>
#elif ATMEGA32U4
#include "MIDIUSB.h"
#endif

const int N_POTS = 4; // total numbers of pots
const int POT_ARDUINO_PIN[N_POTS] = {A0, A1, A2, A3};

int potCState[N_POTS] = {0}; // Current state of the pot
int potPState[N_POTS] = {0}; // Previous state of the pot
int potVar = 0; // Difference between the current and previous state of the pot

int midiCState[N_POTS] = {0}; // Current state of the midi value
int midiPState[N_POTS] = {0}; // Previous state of the midi value

const int TIMEOUT = 300; // Amount of time the potentiometer will be read after it exceeds the varThreshold
const int varThreshold = 10; // Threshold for the potentiometer signal variation
boolean potMoving = true; // If the potentiometer is moving
unsigned long PTime[N_POTS] = {0}; // Previously stored time
unsigned long timer[N_POTS] = {0}; // Stores the time that has elapsed since the timer was reset

// MIDI
byte midiCh = 0; // MIDI channel to be used
byte note = 36; // Lowest note to be used
byte cc = 1; // Lowest MIDI CC to be used

// SETUP
void setup() {

  Serial.begin(115200);

#ifdef DEBUG
  Serial.println("Debug mode");
  Serial.println();
#endif
}

#ifdef pin13
if (i == pin13index) {
  buttonCState[i] = !buttonCState[i]; // inverts the pin 13 because it has a pull down resistor instead of a pull up
}
#endif

// Sends the MIDI note ON accordingly to the chosen board
#ifdef ATMEGA328
// use if using with ATmega328 (uno, mega, nano...)
MIDI.sendNoteOn(note + i, 127, midiCh); // note, velocity, channel

#elif ATMEGA32U4
// use if using with ATmega32U4 (micro, pro micro, leonardo...)

#elif TEENSY
//do usbMIDI.sendNoteOn if using with Teensy
usbMIDI.sendNoteOn(note + i, 127, midiCh); // note, velocity, channel

#elif DEBUG
Serial.print(i);
Serial.println(": button on");
#endif

// Sends the MIDI note OFF accordingly to the chosen board
#ifdef ATMEGA328
// use if using with ATmega328 (uno, mega, nano...)
MIDI.sendNoteOn(note + i, 0, midiCh); // note, velocity, channel

#elif ATMEGA32U4
// use if using with ATmega32U4 (micro, pro micro, leonardo...)

#elif TEENSY
//do usbMIDI.sendNoteOn if using with Teensy
usbMIDI.sendNoteOn(note + i, 0, midiCh); // note, velocity, channel

#elif DEBUG
Serial.print(i);
Serial.println(": button off");
#endif

/////////////////////////////////////////////
// POTENTIOMETERS
void potentiometers() {


  for (int i = 0; i < N_POTS; i++) { // Loops through all the potentiometers

    potCState[i] = analogRead(POT_ARDUINO_PIN[i]); // reads the pins from arduino

    midiCState[i] = map(potCState[i], 0, 1023, 0, 127); // Maps the reading of the potCState to a value usable in midi

    potVar = abs(potCState[i] - potPState[i]); // Calculates the absolute value between the difference between the current and previous state of the pot

    if (potVar > varThreshold) { // Opens the gate if the potentiometer variation is greater than the threshold
      PTime[i] = millis(); // Stores the previous time
    }

    timer[i] = millis() - PTime[i]; // Resets the timer 11000 - 11000 = 0ms

    if (timer[i] < TIMEOUT) {
      potMoving = true;
    }
    else {
      potMoving = false;
    }

    if (potMoving == true) { // If the potentiometer is still moving, send the change control
      if (midiPState[i] != midiCState[i]) {

        // Sends the MIDI CC accordingly to the chosen board
#ifdef ATMEGA328
        MIDI.sendControlChange(cc + i, midiCState[i], midiCh); // cc number, cc value, midi channel

#elif ATMEGA32U4

        controlChange(midiCh, cc + i, midiCState[i]); //
        MidiUSB.flush();

#elif TEENSY
        usbMIDI.sendControlChange(cc + i, midiCState[i], midiCh); // cc number, cc value, midi channel

#elif DEBUG
        Serial.print("Pot: ");
        Serial.print(i);
        Serial.print(" ");
        Serial.println(midiCState[i]);
        //Serial.print("  ");
#endif

        potPState[i] = potCState[i]; // Stores the current reading of the potentiometer to compare with the next
        midiPState[i] = midiCState[i];
      }
    }
  }
}

#ifdef ATMEGA32U4

// Arduino (pro)micro midi functions MIDIUSB Library
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}
#endif

 

]]>
Pomodoro Productivity Phone Stand https://courses.ideate.cmu.edu/60-223/f2022/work/pomodoro-productivity-phone-stand/ Thu, 27 Oct 2022 03:12:10 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16378 I created a phone stand that helps me stay focused when I should be working, making sure I’m not using my phone by emitting an annoying beep when my phone is not on the phone stand.

Images + Videos of Project

Front view of the phone stand.

Overall photo for proportion and scale.

Detail Photos

Detail photo of the electronics on the back of the phone stand.

Detail photo of the potentiometer cutout.

Detail photo of breadboard and Arduino connections.

Process

I wouldn’t say that there were significant milestones within the project, as from the beginning I had a good idea of the steps I needed to take. I would say that I broke the project down into smaller parts, however. 

First, I got the LCD display to count down values according to a Pomodoro timer. This, in my opinion, was the most challenging part, as my software skills aren’t particularly strong, and I had to manipulate some event timers to get it to work. I combined that with a potentiometer, and I had a changeable Pomodoro display. 

Then, I did some wrestling with the IR sensor, and after the hardware challenges I had to figure out with the resistors, and non-functional IR sensors, I included the distance value to my LCD display. 

The final step was adding a buzzer, which wasn’t particularly challenging hardware nor software wise, and incorporating my electronics with my laser cut phone stand, which I had completed beforehand. 

Process Images

Ideation sketches for the laser cutting component of the phone stand.

Trying (unsuccessfully) to get potentiometer + IR sensor + LCD all working in conjunction

Functioning electronics of the IR sensor.

Discussion

I would say that overall, I think my project went about as well as I expected it to go. 

One critique I received was that the phone stand might be improved if “the wires and electronics  were hidden in some type of enclosure”, and I would say that I definitely agree with that comment, as the back of the phone stand worked, but didn’t look very nice. When I was designing my device, the aesthetics were one of my last considerations, and I simply thought that by hiding all the wires behind the phone stand, to the user, the wiring wouldn’t be too obvious and not that much of a concern. While I think that held true, looking back, when I laser cut my phone stand, I could have easily incorporated some sort of box to store my electronics. Though I don’t expect to make another iteration of this project, if I were to make another phone stand, I would probably include some sort of box to store the electronics behind the phone stand, and I would also recut my cutouts for the LCD display/potentiometer to the correct measurements. Most of my focus in this project focused on the technical elements of the project, as I thought that my limitations in coding and hardware would make the project a bit challenging for me. With the help of Zach, I learned how to laser cut, and the coding wasn’t as challenging as I thought it would be, as using the event loops and different conditions, I managed to make a semi-working phone stand. Though my code did have bugs, it was mostly functional, so I was quite pleasantly surprised with my skills. 

Another comment I received during the critique stated “The distance sensor was very accurate and the design as simple yet effective”, which I appreciated. Lots of comments commented on the simplicity and cleanliness of my design, and I would also agree with those comments. All in all, I think I learned a lot from this project, as it allowed me to build my skills with software as well as prototyping, and laser cutting. I think the idea I had from the beginning was pretty straightforward, and I had a good vision for what I needed to accomplish and reasonable expectations for myself, which contributed to the successes of my project.

Schematic Diagram

Block Diagram

Code

/*
 * Pomodoro Productivity Phone Stand
 * Frances Adiwijaya
 * 
 * Description: 
 * 
 * This code controls the arduino behind a pomodoro phone stand. 
 * It will display, on an LCD, the work/break time left in varying
 * pomodoro intervals, changeable by a potentiometer. 
 * It will also use a speaker to beep when the phone is far from an 
 * IR sensor, and the LCD will display a message to tell the user
 * to return the phone to the phone stand
 * 
 * Pin Mapping: 
 * 
 * Arduino Pin   |   Description
 * ------------------------------
 * A0            |   Potentiometer 
 * A3            |   Infrared Sensor
 * 6             |   Buzzer
 * SDA           |   SDA pin on LCD
 * SCL           |   SCL pin on LCD 
 * 
 * Sources: 
 * 
 * LCD Code: 
 * https://courses.ideate.cmu.edu/60-223/f2022/tutorials/I2C-lcd
 * IR Code: 
 * https://courses.ideate.cmu.edu/60-223/f2022/tutorials/IR-proximity-sensor
 * Event Timer Code: 
 * https://courses.ideate.cmu.edu/60-223/f2022/tutorials/code-bites#blink-without-blocking
 * Buzzer Code:
 * https://create.arduino.cc/projecthub/SURYATEJA/use-a-buzzer-module-piezo-speaker-using-arduino-uno-89df45
 * 
 */

//import necessary libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C screen(0x27, 16, 2);

// set up the (constant) variables
// for the mode: work = 0, break = 1, longbreak = 2
int mode = 0;
int breakcount = 0;
unsigned long workTimer = 0;
unsigned long breakTimer = 0;
unsigned long longbreakTimer = 0;
unsigned long updateTimer = 0;
long UPDATEWAIT = 1000;
const int POTPIN = A0;
const int IRPIN = A3;
long interval = 1000;
long BREAKWAIT = 0;
long WORKWAIT = 0;
long LONGBREAKWAIT = 0;
int breakcounter = BREAKWAIT / 1000;
int workcounter = WORKWAIT / 1000;
int longbreakcounter = LONGBREAKWAIT / 1000;
const int BUZZERPIN = 6;


// initialize screen and pins
void setup() {
  // put your setup code here, to run once:
  pinMode(POTPIN, INPUT);
  pinMode(IRPIN, INPUT);
  Serial.begin(9600);
  Serial.println("starting...");
  screen.init();
  screen.backlight();
  pinMode(BUZZERPIN, OUTPUT);

}

void loop() {
  // check the value of the potentiometer and map it to the time intervals
  unsigned long potVal;
  potVal = analogRead(POTPIN);
  potVal = map(potVal, 0, 1023, 1000, 30000);
  unsigned long breaktime = potVal / 5;

  int distanceVal = analogRead(IRPIN);
  Serial.println(distanceVal);

  // create the pomodoro intervals
  BREAKWAIT = breaktime;
  WORKWAIT = potVal;
  LONGBREAKWAIT = breaktime * 4;



  // put your main code here, to run repeatedly:
  // if we're in break mode:
  if (mode == 1) {
    // if the break timer is up enter the statement
    if ((millis() - breakTimer >= BREAKWAIT)) {
      Serial.println("switching to work");
      // increment the short break count
      breakcount += 1;
      // reset all timers
      longbreakTimer = millis();
      workTimer = millis();
      breakTimer = millis();
      // recheck the intervals
      breakcounter = BREAKWAIT / 1000;
      workcounter = WORKWAIT / 1000;
      longbreakcounter = LONGBREAKWAIT / 1000;
      // switch to work mode
      mode = 0;
    }
    // update the LCD display every second, and count down
    if ((millis() - updateTimer >= UPDATEWAIT)) {
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("break: ");
      breakcounter = breakcounter - 1;
      Serial.println(breakcounter);
      screen.print(breakcounter+1);
      updateTimer = millis();
    }
  }
  // if we're in work mode:
  if (mode == 0) {
    // if the work timer is up
    if ((millis() - workTimer >= WORKWAIT)) {
      //check if we're on a break or a long break
      //if next is a longbreak, enter the statement
      if (breakcount > 3) {
        Serial.println("switching to longbreak");
        // reset all timers
        longbreakTimer = millis();
        workTimer = millis();
        breakTimer = millis();
        breakcounter = BREAKWAIT / 1000;
        workcounter = WORKWAIT / 1000;
        longbreakcounter = LONGBREAKWAIT / 1000;
        // switch to longbreak mode
        mode = 2;
      }
      //if next is a regular break, enter the statement
      else {
        Serial.println("switching to break");
        // reset all timers
        longbreakTimer = millis();
        workTimer = millis();
        breakTimer = millis();
        breakcounter = BREAKWAIT / 1000;
        workcounter = WORKWAIT / 1000;
        longbreakcounter = LONGBREAKWAIT / 1000;
        // switch to break mode
        mode = 1;
      }
    }
    // if the IR distance sensor registers a value too large, enter the loop
    if ((distanceVal < 200)) {
      // display a message on the LCD
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("Put Phone Back!");
      Serial.println("IR Val");
      Serial.println(distanceVal);
      // beep the buzzer
      delay(200);
      tone(BUZZERPIN, 1000); // Send 1KHz sound signal...
      delay(1000);        // ...for 1 sec
      noTone(BUZZERPIN);     // Stop sound...
      delay(1000);        // ...for 1sec
    }
    // update LCD to count down every second
    if ((millis() - updateTimer >= UPDATEWAIT)) {
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("work: ");
      workcounter = workcounter - 1;
      Serial.println(workcounter);
      screen.print(workcounter);
      updateTimer = millis();
    }
  }
  // if we're in longbreak mode
  if (mode == 2) {
    // if the timer is over, enter the statement
    if (millis() - longbreakTimer >= LONGBREAKWAIT) {
      Serial.println("switching to work");
      // reset the breakcount
      breakcount = 0;
      // reset all timers
      longbreakTimer = millis();
      workTimer = millis();
      breakTimer = millis();
      breakcounter = BREAKWAIT / 1000;
      workcounter = WORKWAIT / 1000;
      longbreakcounter = LONGBREAKWAIT / 1000;
      // switch to work mode
      mode = 0;
    }
    // count down on LCD
    if ((millis() - updateTimer >= UPDATEWAIT)) {
      screen.clear();
      screen.setCursor(0, 0);
      screen.print("long break: ");
      longbreakcounter = longbreakcounter - 1;
      Serial.println(longbreakcounter);
      screen.print(longbreakcounter+1);
      updateTimer = millis();
    }
  }
}

 

]]>
Study Buddy https://courses.ideate.cmu.edu/60-223/f2022/work/study-buddy/ Thu, 27 Oct 2022 02:47:33 +0000 https://courses.ideate.cmu.edu/60-223/f2022/work/?p=16427 By: Jonathan Lindstrom

What is it?

The Study Buddy helps keeps you focused while studying and gives you a sweet reward after intense study sessions.

Discussion

For my project, I feel many things went well but there was also a lot that could be improved upon. Starting with the positive critiques I was happy to hear that “The interface was excellent. Super intuitive to use. And so well contained in the box!” That was one of my primary focuses in my project. I wanted to create an interface that anybody could pick up and be able to navigate. Creating the UI was my favorite part of the project. Working with it I learned a lot about what makes an effective UI. In the future, I think I would code it a little differently to save some memory on the Arduino but overall I was very proud of it.

For negative critiques many of the comments regarded the LED indicator, my classmates mentioned “I should possibly add more LEDs to signify if the user was in work time or break time, or having multiple colors of LEDs to make the design a bit more intuitive. “ I agree and it is something that I wish I had properly implemented. I was planning to have different colors for work and break but I simply ran out of time and it wasn’t a priority for me.

There were a couple of things I wish I had done differently or better. I wish I had implemented the different colored LEDs as I feel it would have made the light indicator more effective. I also would have liked to integrate the candy dispensing mechanism into the main body of the project as opposed to building it on the side.

I do not think I will build a second iteration but these would be the next steps if I were to build a second interaction of this project. Furthermore, when I performed some user tests I realized that the fact that a joystick is a button isn’t necessarily intuitive. I had users attempting to press the home button to select options in my menu. This led me to glue on little signs near my buttons to make it more intuitive but if there were a second iteration I would laser cut these messages.

Overall I was pleased with how my project turned out. It functions largely how I intended it to and I had a lot of fun designing the UI and laser cutting the housing for the electronics.

Images & Videos

-Final Image

The final images of my Study Buddy.

-Working Video

Note: Demo time in this video is sped up to 10x speed so the full functionality can be shown.

-Detail Photos

The LED on the top of the box serves two functions. Firstly it is an indicator that the device should be on and working and it also blinks when the user has paused their study time or when it is transitioning between work and break time.

The joystick is used to navigate the user interface. The code is constantly checking the position of the joystick as well as when it is pressed so it can perform the functions the user desires.

Candy Dispenser built out of a servo and cardboard. It is attached to the body using hot glue and balsa wood and the servo wires are fed in through a small hole.

-Use Photos

The screen for changing values. Can scroll up or down to increase/decrease the total time spent studying. There are the same screens for work time and break time.

The screen for studying. Counts you down and alternates between your work and break times until the total time is done.

Once you are done the screen displays “Great Study Session!” and dispenses the candy.

Process Images and Review

-Process Images

These were some of the first drawings of how I wanted the UI to look. Some changes were made as I went but you can see how these influenced the interface in the study buddy.

The start of my menu. Another section was added later, Demo Mode, but I started by just including the main function and directions pages.

This was the hardest section to figure out. I had to create new pages for each of the three values and be able to change and save them returning to this screen.

-Review / Decision Points

1. The Candy Shoot

This is a preliminary sketch. The original Plan was to incorporate the candy shoot and servo into the main box.

This was replaced with an external servo and candy shoot after I struggled to properly incorporate a working design into the main body.

2. How to keep track of time

The original plan was to keep track of time with a Real Time Clock Module. This module can be seen in some of my process photos. However I realized this was difficult to implement and I did not need to keep track of the actual time, just how long a minute is. So I opted to take this out and keep track of my time using the millis() function. 1000 milliseconds = 1 second so I could use millis to keep track of the seconds and count down. I found it much easier to implement and overall simpler for the purposes of my project.

Schematic & Block Diagram

Schematic

Electrical schematic of the Study Buddy.

Block Diagram

Block Diagram for the different inputs/outputs in the Study Buddy.

Code

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

///----THE STUDY BUDDY-----///
//----Jonathan Lindstrom---//

//This project is intended to help an individual study more
//Productively. It implements an intuitive UI to help the
//User set the total time they want to study for, the time for working, and the time for breaks
//The default is the pomodoro method which is 25 minutes of work with a 5 minute break.
//The user is also able to pause at any point for a break by pressing the joystick.
//Once total time is up it triggers a servo motor releasing some candy for a reward.

//-----PIN MAPPING--------//
//| Joy X Axis -->    A0   |
//| Joy X Axis -->    A1   |
//| Joy Button -->    2    |
//| Servo      -->    5    |
//| Green LED  -->    6    |
//| LCD Pins   --> SDA/SCL |

//-----VARIABLES-------///

//----CONSTANTS----//
Servo gaugeMotor;
const int QUARTERWAIT = 250;
const int VRX = A0;
const int VRY = A1;
const int SW = 2;
const int HOMEBUT = 9;
const int GREEN = 6;
const int SERVO = 5;

//--MAIN FUNCTIONS---//
String joyPos = ""; //Gets the joy position as "UP, DOWN, LEFT, OR RIGHT" Depending on joystick position
String screenState = "HOME"; //HOME, DIRECTIONS, OPTIONS, TOTAL, STUDY, BREAK, STUDYING, Used to tell the arduino which screen to display


//-----NUMS TO REPRESENT THE DIFFERENT SCREENS----//
int screenHome = 0;//0-1 for 2 options
int screenOptions = 0; //0-3 for 4 options
int screenDirections = 0; //0-3 for 4 options
boolean screenStudying = false;

//----INPUT VARIABLES-----??
int joyOnlyOnce = 0;//Makes sure that numbers can only change only once everytime the joystick is pressed
int joyButOnce = 0; //Makes sure joy button is only pressed once
int buttonState = 0; //State of JoyStick Button
int homeButtonState = 0; //Keeps Track of Button State of Home Button

//---VARIABLES FOR TIME/TIME CHECKS----//
int totalTime = 60; //Total Time with Studys AND Breaks
int studyTime = 25; //Total Time of each sec studying
int breakTime = 5; //Total Time of each break
boolean place = true; //Placeholder booleans that allow for
boolean pause = false; //Keeping make sure specific parts of the code run only once
boolean light = true;
int check = 1;
int totalPlace = totalTime; //Make a copy of totalTime that can be altered
long mins = - 1;
long sec = 60;
int millPerSec = 1000; //Used to change for Demo Mode

//---LCD VARIABLES----//
unsigned long quarterTimer = 0;
LiquidCrystal_I2C screen(0x27, 20, 4); //Initilize screen



void setup() {
  gaugeMotor.attach(SERVO);
  Serial.begin(9600);
  screen.init();
  screen.backlight();

  pinMode(VRX, INPUT);
  pinMode(VRY, INPUT);
  pinMode(SW, INPUT_PULLUP);
  pinMode(HOMEBUT, INPUT);
  pinMode(GREEN, OUTPUT);
  digitalWrite(GREEN, HIGH);
}

void loop() {
  millis(); //Start Millis
  joyPosition(); //Call JoyStick Functions in loops so the values are always changing with accordance to joystick position and Screen
  joyRight();
  joyLeft();
  joyUp();
  joyDown();
  joyButtonPressed(); //Checks all cases in which pressing the Joystick will cause a change in screen
  homeButtonPressed(); //Checks if at any point the home button is pressed

  if (pause) { //Makes light blink when in pause
    blinkLight();
  }
  if (!pause) { //LED solid green otherwise when the device is on
    digitalWrite(GREEN, LOW);
  }

  //LCD will change depending on the screenState and display the right screen accordingly. This is constantly changing.
  if (screenState == "HOME") {
    homeScreen();
    resetVals();
  }

  if (screenState == "DIRECTIONS") {
    printDirections();
  }

  if (screenState == "OPTIONS") {
    optionsScreen();
  }

  if (screenState == "TOTAL") {
    totaltimeScreen();
  }

  if (screenState == "STUDY") {
    studytimeScreen();
  }

  if (screenState == "BREAK") {
    breaktimeScreen();
  }
  if (screenState == "STUDYING") {
    studyingScreen();
    timeCheck();//Keep Track of Mins and Secs with millis(). Use when study screen is brought up;
  }
  if (screenState == "COMPLETE") {
    completeScreen();
  }

}



//==========JOYSTICK FUNCTIONS==========//
void joyPosition() {
  if (analogRead(VRX) > 900) {
    joyPos = "LEFT";
  }
  else if (analogRead(VRX) < 100) {
    joyPos = "RIGHT";
  }
  else if (analogRead(VRY) < 100) {
    joyPos = "DOWN";
  }
  else if (analogRead(VRY) > 900) {
    joyPos = "UP";
  }
  else {
    joyPos = "NEUTRAL";
    joyOnlyOnce = 0;
  }

}
void homeButtonPressed() { //Will go to home screen from any point in the code
  homeButtonState = digitalRead(HOMEBUT);
  if (homeButtonState) {
    screenState = "HOME";
    totalTime = 60; 
    studyTime = 25; 
    breakTime = 5; 
    millPerSec = 1000; //Resets actual mill of a second to 1000 --> Demo Mode
    pause = false;
    check = 1;
    screenDirections = 0;

    screen.clear();
  }
}
void joyButtonPressed() { //Performs all functions for the Joystick being pressed
  buttonState = digitalRead(SW);
  if (buttonState == 0) {
    if (joyButOnce == 0) {
      joyButOnce = 1;
      screen.clear();

      if (screenState == "HOME") {
        if (screenHome == 1) {
          screenState = "DIRECTIONS";
        }
        if (screenHome == 0) {
          screenState = "OPTIONS";
        }
        if (screenHome == 2) {
          demo();
        }
      }

      else if (screenState == "OPTIONS") {
        if (screenOptions == 0) {
          screenState = "STUDYING";
          setMins();
        }
        if (screenOptions == 1) {
          screenState = "TOTAL";
        }
        if (screenOptions == 2) {
          screenState = "STUDY";
        }
        if (screenOptions == 3) {
          screenState = "BREAK";
        }
      }

      else if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
        screenState = "OPTIONS";
      }

      else if (screenState == "STUDYING") {
        pause = !pause;
      }


    }
  }

  if (buttonState == 1) {
    joyButOnce = 0;
  }
}


void joyUp() { //Performs all functions and changes when the Joystick is pushed up
  if (joyPos == "UP") {
    if (screenState == "HOME") {
      if (joyOnlyOnce == 0) {
        screenHome -= 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenHome <= -1) {
          screenHome = 2;
        }
      }
    }
    if (screenState == "OPTIONS") {
      if (joyOnlyOnce == 0) {
        screenOptions -= 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenOptions <= -1) {
          screenOptions = 3;
        }
      }
    }
    if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
      if (joyOnlyOnce == 0) {
        if (screenState == "TOTAL") {
          totalTime += 5;
          totalPlace = totalTime;
        }
        if (screenState == "STUDY") {
          studyTime += 5;
        }
        if (screenState == "BREAK") {
          breakTime += 5;
        }
        screen.clear();
        joyOnlyOnce = 1;
        if (totalTime > 120) {
          totalTime = 0;
          totalPlace = totalTime;
        }
        if (studyTime > 60) {
          studyTime = 0;
        }
        if (breakTime > 30) {
          breakTime = 0;
        }
      }
    }



  }
}

void joyDown() { //Performs all functions and changes when the Joystick is pushed down
  if (joyPos == "DOWN") {
    if (screenState == "HOME") {
      if (joyOnlyOnce == 0) {
        screenHome += 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenHome >= 3) {
          screenHome = 0;
        }
      }
    }
    if (screenState == "OPTIONS") {
      if (joyOnlyOnce == 0) {
        screenOptions += 1;
        screen.clear();
        joyOnlyOnce = 1;
        if (screenOptions >= 4) {
          screenOptions = 0;
        }
      }
    }

    if (screenState == "TOTAL" || screenState == "STUDY" || screenState == "BREAK") {
      if (joyOnlyOnce == 0) {
        if (screenState == "TOTAL") {
          totalTime -= 5;
          totalPlace = totalTime;
        }
        if (screenState == "STUDY") {
          studyTime -= 5;
        }
        if (screenState == "BREAK") {
          breakTime -= 5;
        }
        screen.clear();
        joyOnlyOnce = 1;
        if (totalTime < 0) {
          totalTime = 120;
          totalPlace = totalTime;
        }
        if (studyTime < 0) {
          studyTime = 60;

        }
        if (breakTime < 0) {
          breakTime = 30;
        }
      }
    }


  }
}

void joyRight() { ////Performs all functions and changes when the Joystick is pushed right
  if (joyPos == "RIGHT") {
    if (screenState == "DIRECTIONS") {
      if (joyOnlyOnce == 0) { //Navigate Directions
        screen.clear();
        screenDirections += 1;
        joyOnlyOnce = 1;
        if (screenDirections >= 4) {
          screenDirections = 0;
        }
      }
    }
  }
}

void joyLeft() { //Performs all functions and changes when the Joystick is pushed left
  if (joyPos == "LEFT") {
    if (screenState == "DIRECTIONS") {
      if (joyOnlyOnce == 0) { //Navigate Directions
        screen.clear();
        screenDirections -= 1;
        joyOnlyOnce = 1;
        if (screenDirections <= -1) {
          screenDirections = 3;
        }
      }
    }
  }
}



//===========SCREENS===========//

void directions1() { //Directions screen 1
  screen.setCursor(0, 0);
  screen.print("Directions(1/4):");
  screen.setCursor(0, 1);
  screen.print("-Use stick to scroll");
  screen.setCursor(0, 2);
  screen.print("through directions");
  screen.setCursor(0, 3);
  screen.print("<-- stick = joystick");
}

void directions2() { //Directions screen 2
  screen.setCursor(0, 0);
  screen.print("Directions(2/4):");
  screen.setCursor(0, 1);
  screen.print("-Press button to");
  screen.setCursor(0, 2);
  screen.print("right for home -->");
  screen.setCursor(0, 3);
  screen.print("-Press on joy for Ok");
}

void directions3() { //Directions screen 3
  screen.setCursor(0, 0);
  screen.print("Directions(3/4):");
  screen.setCursor(0, 1);
  screen.print("-Numbers");
  screen.setCursor(0, 2);
  screen.print("Up for increase");
  screen.setCursor(0, 3);
  screen.print("Down for decrease");
}

void directions4() { //directions screen 4
  screen.setCursor(0, 0);
  screen.print("Directions(4/4):");
  screen.setCursor(0, 1);
  screen.print("Ok to pause timer");
  screen.setCursor(0, 3);
  screen.print("Press Home :)");
}

void printDirections() { //ScreenState DIRECTIONS, Changes the directions page accordingly
  if (screenDirections == 0) {
    directions1();
  }
  if (screenDirections == 1) {
    directions2();
  }
  if (screenDirections == 2) {
    directions3();
  }
  if (screenDirections == 3) {
    directions4();
  }
}

void homeScreen() { //ScreenState HOME
  screen.setCursor(0, 0);
  screen.print("Home:");
  screen.setCursor(4, 1);
  screen.print("Start Studying");
  screen.setCursor(4, 2);
  screen.print("Directions");
  screen.setCursor(4, 3);
  screen.print("Demo Mode");
  if (screenHome == 0) {
    screen.setCursor(0, 1);
  }
  if (screenHome == 1) {
    screen.setCursor(0, 2);
  }
  if (screenHome == 2) {
    screen.setCursor(0, 3);
  }
  screen.print("-->");
}

void optionsScreen() { //ScreenState OPTIONS

  screen.setCursor(0, 0);
  screen.print("Options:");
  screen.setCursor(3, 1);
  screen.print("Time Total = ");
  screen.setCursor(3, 2);
  screen.print("Time Study = ");
  screen.setCursor(3, 3);
  screen.print("Time Break = ");
  screen.setCursor(15, 0);
  screen.print("OK?");

  if (screenOptions == 0) {
    screen.setCursor(12, 0);
  }
  if (screenOptions == 1) {
    screen.setCursor(0, 1);
  }
  if (screenOptions == 2) {
    screen.setCursor(0, 2);
  }

  if (screenOptions == 3) {
    screen.setCursor(0, 3);
  }
  screen.print("-->");
  screen.setCursor(16, 1);
  screen.print(totalTime);
  screen.setCursor(16, 2);
  screen.print(studyTime);
  screen.setCursor(16, 3);
  screen.print(breakTime);
}

void totaltimeScreen() { //Screen State TOTAL
  screen.setCursor(0, 0);
  screen.print("Total Time:");
  screen.setCursor(13, 0);
  screen.print(totalTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}
void studytimeScreen() { //ScreenState STUDY
  screen.setCursor(0, 0);
  screen.print("Study Time:");
  screen.setCursor(13, 0);
  screen.print(studyTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}

void breaktimeScreen() { //ScreenState BREAK
  screen.setCursor(0, 0);
  screen.print("Break Time:");
  screen.setCursor(13, 0);
  screen.print(breakTime);
  screen.setCursor(0, 2);
  screen.print("Up/Down to change");
  screen.setCursor(0, 3);
  screen.print("When Done Press Ok");
}

void studyingScreen() {//ScreenState STUDYING
  screen.setCursor(0, 0);
  if (screenStudying) {
    screen.print("Study: "); //Changes depending on if user is studying or on break
  }
  if (!screenStudying) {
    screen.print("Break: ");
  }

  screen.setCursor(7, 0);
  screen.print(mins / 10);
  screen.setCursor(8, 0);
  screen.print(mins % 10);
  screen.setCursor(9, 0);
  screen.print(":");
  screen.setCursor(10, 0);
  screen.print(sec / 10);
  screen.setCursor(11, 0);
  screen.print(sec % 10);
  screen.setCursor(0, 1);
  screen.print("--------------------");
  screen.setCursor(0, 2);
  if (pause) {
    screen.print("Press Ok to Continue");
  }
  else if (screenStudying) {
    screen.print("Good Luck! Work Hard");
  }
  else if (!screenStudying) {
    screen.print("Enjoy Your Break!   ");
  }
  screen.setCursor(0, 3);
  screen.print("Home=Quit, Ok=Pause");


}

void completeScreen() { //Displays completed when the user finishes. Dispenses candy, ScreenState COMPLETE
  screen.setCursor(0, 0);
  screen.print("GREAT STUDY SESSION!");
  screen.setCursor(0, 2);
  screen.print("Candy Dispensing :)");
  screen.setCursor(5, 3);
  screen.print("Press Home");
  if (check == 1) { //Makes sure the candy code only runs once each COMPLETE screen
    gaugeMotor.write(0);
    delay(5000); //Delay to make sure that the candy is properly dispensed and servo reset before the user can go home or start studying again
    gaugeMotor.write(90);
    check = 0;
  }
}

//=========TIME & DEMO FUNCTIONS======//

void timeCheck() { //Keeps track of time using millis to count seconds and minutes
  static long t = 0;
  if (!pause) {
    if (millis() - t >= millPerSec) {
      t = millis();
      //1 Second Passing
      sec -= 1;
      if (sec == 0) {
        if (mins == 0 && totalPlace <= 0) {
          screen.clear();
          screenState = "COMPLETE";
        }
        mins -= 1;
        sec = 60;
      }
    }
    if (mins == -1) {
      setMins(); //Onces mins set runs out it changes it accordingly to breakTime/StudyTime
    }
  }
}

void setMins() { //resets mins alternating between break and studying when called
  if (place) {
    mins = studyTime - 1;
    place = !place;
    screenStudying = !screenStudying;
  }

  else {
    mins = breakTime - 1;
    place = !place;
    screenStudying = !screenStudying;
  }
  if (totalTime != totalPlace && totalPlace != 0) { //Only runs this after the start
    pause = true;
  }
  totalPlace -= mins + 1;

}

void resetVals() { //RESETS VALS
  mins = studyTime - 1;
  sec = 60;
  place = true;
  totalPlace = totalTime;
  screenStudying = false;
}

void demo() { //Demo mode
  millPerSec = 100;
  totalTime = 3;
  totalPlace = totalTime;
  studyTime = 1;
  breakTime = 1;
  screenState = "OPTIONS";
}

void blinkLight() { //Causes Green LED To Blink
  static int t = 0; //For alt between break/study and for pauses
  if (millis() - t >= 250) {
    if (light) {
      digitalWrite(GREEN, LOW);
      light = !light;
    }
    else if (!light) {
      digitalWrite(GREEN, HIGH);
      light = !light;
    }
    t = millis();


  }
}

 

 

 

 

]]>