An overall photo for portion and scale.
The “self destruct” button
The LED strip on a wall of the box.
The Hour and Minute dials as well as the Set button.
The use of the LCD screen. This shows the main feature which is the Set Timer feature, it also shows the top unlocked (i.e. the servo lock is down)
This was a decision point when I determined that two rotary encoders was better than one. This was a big decision because I always intended to use 1 and utilize the button feature on the encoder to switch between hours and minutes – but I realized that using 2 was much more efficient.
This was a major decision point. Due to how hard it was for me to get the timer to work properly, I had originally scrapped the idea of a self destruct button. However, I really wanted to cover the use case of me needing my phone before time was up so I took a leap even though I was low on time – and included the self-destruct button/all of it’s code.
This is right when I started the assignment. The goal at this point was to get all of the components functioning on the Arduino singularly and then to have them signal to each other.
At this point, all of the components worked (servo, LED strip, etc.) except the timer didn’t function properly so nothing worked “together”.
This is after Zach walked me through the logic of the timer and I was finally able to code it so it functioned properly.
This was the point at which the electrical components all worked, and the box had been laser cut, and the biggest concern was unwiring and rewiring everything to fit nicely inside the box, as well as getting the box to stay together as I did that.
The in-class critiques that I got were mostly just positive attributes that classmates appreciated about the design. It didn’t really give me much to move forward with, but it was good to have my design decisions validated. One person commented, “I thought the name was very cool and I liked the use of a tactile button to control the timer.” I really liked the idea of the project and what I was making so I put a lot of time into designing based on what I would like to use. I’ve always wanted an “inator” so it seemed like a perfect time to create something like that… thus the name. Another person commented “It looks like a lot of the components of your project are working really well together. I also like the LED use”. When conceptualizing this assignment, I wanted to create something simple enough that I could do everything I needed to do within the ~two week span we were allocated. Thus, all of the components of my project – the LED strip, the LCD screen and timer, the self-destruct button… It was all in an effort to finish the project on time and create something I’d want to use. So I appreciated this comment because I sought to be very intentional.
This project was unexpectedly challenging. I anticipated a walk in the park on a day with old snow (fumbling/tripping on dirty snow, a bit of slipping on ice – but ultimately being fine), but instead my casual walk included me getting wrapped up in a tornado and spit out at the end of the park. Thus, all of the components (LED strip, buttons/dials, etc.) came together somewhat easily and overall didn’t take more time than anticipated… but the timer was the tornado. I didn’t expect the logic and coding of it to be some math-heavy and difficult. I worked an entire weekend without getting anywhere, and restarted the code from scratch twice to make it work. In retrospect, I would tell myself to not overthink the logic and to go down the math route rather than scenario planning for each individual component of the timer (hour, min, sec). I also wish I hadn’t gotten too hung up on the timer – I think it would have benefitted me more to move onto the actual box/laser cutting while I waited and tried to debug the timer. However once the timer was completed, all the other components came together within two days and there were no more major storms on my walk.
Overall, I’d say I’m very proud of this project. It satisfied all of my goals and created a project that I honestly intend to use for years to come. In fact, I’d say I satisfied my goals almost to a fault. After seeing other people’s projects – I realized I could have gone even further. For example, I have a lamp that has 3 slots for USB’s, so I didn’t include a battery pack or an on/off button on the Nay-Phone Inator, instead I just left it for a USB input because that is what fit my specific needs. Also things like what happens after the self destruct button is pushed. The idea was that after the self-destruct button and phone retrieval, you simply unplug the USB (because why would the device need to be on if you had given up?), but in the final critique some people seemed surprised that there was no way to return to the Set Timer screen after the phone was retrieved. So I think small things like that would actually be my Next Steps. I don’t plan to build another iteration of the project, but if I did – I would address those small details.
/* Nay Phone Inator - Project 2 Noni Shelton Code for a device that locks away your phone for a specified amount of time so you can focus on tasks at hand. Credit to Professor Zach Credit to the creators of the libraries I used */ /* PIN MAPPING pin | mode | description ------|--------|------------ 2 input Rotary Encoder - hour 3 input Rotary Encoder - hour 4 input The lock (the Servo motor) 5 input The "Set" button 13 input The "self destruct" button 9 input Rotary Encoder - minute 10 input Rotary Encoder - minute */ #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Encoder.h> #include <PololuLedStrip.h> #include <Servo.h> Servo lockMotor; const int SELF_DESTRUCT_BUTTON_PIN = 13; const int SET_BUTTON_PIN = 5; const int LOCKMOTORPIN = 4; unsigned long lcdTimer = 0; unsigned long ledStripTimerSinglecolor = 0; PololuLedStrip<6> ledStrip; // LED STRIP: Create a buffer for holding the colors (3 bytes per color). #define LED_COUNT 60 rgb_color colors[LED_COUNT]; int totalTimerSeconds = 0; int selfDestructButtonState = 0; int destructTimer = 0; bool timerCounting = true; bool countdownMode = false; bool doorLocked = false; bool endScreenPrintSequence = false; bool destructPassed = false; LiquidCrystal_I2C screen(0x27, 16, 2); Encoder knobHour(2, 3); Encoder knobMinute(9, 10); void setup() { // put your setup code here, to run once: Serial.begin(9600); #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // END of Trinket-specific code. //initalizing pins pinMode(SELF_DESTRUCT_BUTTON_PIN, INPUT); lockMotor.attach(LOCKMOTORPIN); //inital timer Serial.begin(9600); screen.init(); screen.backlight(); screen.home(); screen.clear(); //intro screen screen.print("Nay-Phone Inator"); delay(2000); screen.clear(); //set timer screen.home(); screen.print("Set Timer"); screen.setCursor(5, 1); screen.print("00hr:00min"); } void loop() { //setting up the rotary encoders int setTimerHour, setTimerMin; setTimerHour = knobHour.read() / 4; setTimerMin = knobMinute.read() / 4; //hour rotary encoder goes to 12 and loops around if (setTimerHour < 0) setTimerHour = 10; else if (setTimerHour > 10) setTimerHour = 0; //min rotary encoder goes to 12 and loops around if (setTimerMin < 0) setTimerMin = 60; else if (setTimerMin > 59) setTimerMin = 0; //bool sure set timer only prints once if (endScreenPrintSequence == false) { //printing the "Set Timer" changing values if (millis() - lcdTimer >= 250) { screen.clear(); screen.home(); screen.print("Set Timer"); screen.setCursor(5, 1); screen.print("00hr:00min"); //setting hours if (setTimerHour < 10) screen.setCursor(6, 1); else screen.setCursor(5, 1); screen.print(setTimerHour); //setting mins if (setTimerMin < 10) screen.setCursor(11, 1); else screen.setCursor(10, 1); screen.print(setTimerMin); lcdTimer = millis(); } } //LED STRIP: making the LED Strip Flash a rainbow if it's in SetTimer Mode if (countdownMode == false) { //timer updates every 1/4th of a second if (millis() - lcdTimer >= 250) { // Update the colors. uint16_t time = millis() >> 2; for (uint16_t i = 0; i < LED_COUNT; i++) { byte x = (time >> 2) - (i << 3); colors[i] = hsvToRgb((uint32_t)x * 359 / 256, 255, 255); } // Write the colors to the LED strip. ledStrip.write(colors, LED_COUNT); } } //set button pressed -- i.e. moving on to the countdown timer if (digitalRead(SET_BUTTON_PIN) == HIGH) countdownMode = true; if (countdownMode) { //updates every 1 second if (millis() - lcdTimer >= 100) { screen.clear(); screen.home(); screen.print("Timer"); screen.setCursor(4, 1); screen.print("00:00:00"); lcdTimer = millis(); if (timerCounting) { totalTimerSeconds = ((setTimerHour * 3600) + (setTimerMin * 60)); timerCounting = false; } //every second, the hours/mins/seconds are updated totalTimerSeconds--; //updates seconds int seconds = totalTimerSeconds; //updates hours int hourUpdate = seconds / 3600; seconds = seconds - (hourUpdate * 3600); //updates minutes int minUpdate = seconds / 60; seconds = seconds - (minUpdate * 60); if (totalTimerSeconds >= 0) { //servo motor: locking the door -- lockMotor.write(5); //led strip turns red for (uint16_t i = 0; i < LED_COUNT; i++) { colors[i] = rgb_color(255, 0, 0); } //Updating the timer on the LCD Screen //printing the seconds screen.setCursor(10, 1); if (seconds < 10) screen.setCursor(11, 1); else screen.setCursor(10, 1); screen.print(seconds); //printing the mins if (minUpdate < 10) screen.setCursor(8, 1); else screen.setCursor(7, 1); screen.print(minUpdate); //printing the hours if (hourUpdate < 10) screen.setCursor(5, 1); else screen.setCursor(4, 1); screen.print(hourUpdate); //SELF DESTRUCT MODE selfDestructButtonState = digitalRead(SELF_DESTRUCT_BUTTON_PIN); if (selfDestructButtonState == HIGH) { //setting a destruct countdown destructTimer++; //making sure the screen prints the proper thing endScreenPrintSequence = true; if (destructTimer < 10) { //destruct timer displays warning screen.clear(); screen.print("Hold for 10 secs"); screen.setCursor(0, 1); screen.print("to give up"); delay(1000); } else { //passing the destruct bool destructPassed = true; } } if (destructPassed) { //destruct timer displays disappointment screen.clear(); screen.print("Dang. That was"); screen.setCursor(0, 1); screen.print("disappointing."); delay(1000); //servo motor: unlock the door lockMotor.write(90); delay(1000); } } else { //TIMER HAS BEEN ENDED //making sure the screen prints the proper thing endScreenPrintSequence = true; screen.clear(); screen.home(); screen.print("Amazing job! You"); screen.setCursor(0, 1); screen.print("rock homie"); //servo motor: unlock the door lockMotor.write(90); delay(1000); //LED STRIP: updating the LED strip with congratulatory colors // Update the colors. byte time = millis() >> 2; for (uint16_t i = 0; i < LED_COUNT; i++) { byte x = time - 8 * i; colors[i] = rgb_color(x, 255 - x, x); } } // Write the colors to the LED strip. ledStrip.write(colors, LED_COUNT); } } } // THE FUNCTIONS I'M USING // Converts a color from HSV to RGB. // h is hue, as a number between 0 and 360. // s is the saturation, as a number between 0 and 255. // v is the value, as a number between 0 and 255. rgb_color hsvToRgb(uint16_t h, uint8_t s, uint8_t v) { uint8_t f = (h % 60) * 255 / 60; uint8_t p = (255 - s) * (uint16_t)v / 255; uint8_t q = (255 - f * (uint16_t)s / 255) * (uint16_t)v / 255; uint8_t t = (255 - (255 - f) * (uint16_t)s / 255) * (uint16_t)v / 255; uint8_t r = 0, g = 0, b = 0; switch ((h / 60) % 6) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } return rgb_color(r, g, b); }
]]>
The Exercise Set Tracker (EST) helps me keep track of my 5×5 sets during workouts with just the push of a button.
LEDs in use.
Dollar for size and scale comparison.
Dollar for size and scale comparison 2.
EST in use.
One decision point in my process revolved around just how many LEDs the EST should have. Originally I had planned on having the EST feature 10 separate LEDs and having each one of them account for a single repetition of an exercise. It didn’t take long for me to realize that such a set up would quickly change the EST from being a helpful device to an annoying one depending on the exercise, so I cut the amount of LEDs by half. Doing so resulted in the remaining 5 LEDs accounting only for the amount of sets I typically do- making the EST more ‘universal’ in terms of exercise usefulness as I typically do five sets for almost all of my exercises.
The biggest decision point in my process, however, had to do with the physical case containing the EST. In my original design of the EST, it was meant to be as small as possible to the point where it’d be able to fit on my wrist. Given the relative simplicity of the EST (after all, it’s essentially just a button turning on lights), there were many possibilities as to what the physical case containing the EST could look like. I went so far as to acquire the necessary compact microcontroller and event print out a bracelet holder for the EST. Ultimately, however, due to time constraints I had to pivot to an easier to acquire and make ‘box’ set up. The box, whose dimensions are approximately 4.5in x 4.5in x 1.5in, was still compact enough so as to not be inconvenient to bring around but not as compact as I had originally hoped.
All things considered, I would say that I’m moderately content with how the EST turned out. Although it wasn’t exactly close to what I had originally planned in terms of size and form, it does function as intended and its final form is good enough to carry around with me and actually use at the gym. Considering an imposing lack of time due to some personal circumstances, I was honestly kind of surprised the final outcome of the EST was actually quite usable. That being said, it is still far from ideal and as a result I wouldn’t say I’m super happy with it, but I am somewhat satisfied mainly after considering some of the personal constraints I faced throughout the project.
Probably the biggest flaw in the EST is its admittedly unattractive physical case. I agree with a comment from the in-class crit concerning this: “The enclosure is pretty sloppy; this is a sort of project that’s a good candidate for laser cutting a simple little box.” If I had known that my original plan of a small wrist watch-esque form wasn’t going to pan out, I would have instead opted for 3D-printing a nice case for the EST. Ultimately the sloppy little box was a result of last minute necessity, and I totally agree that laser cutting would have been useful here. One of my errors during this project was my desire to stay away from laser cutting and use 3D-printing instead. The latter simply required more time which I just didn’t have, and in retrospect I would have just stuck with laser cutting if I had known 3D-printing wasn’t going to work out.
Something I overlooked regarding the EST was its button; another piece of feedback I got from the in-class critique was: “I’d make your button much easier to mash on indiscriminately rather than needing to carefully push on it”. I fully agree with this and hearing this feedback made me realize that I had not considered the button component of the EST nearly as much as I should’ve. Rather than going with the first button prepared I saw, I should’ve instead considered different button setups. I really do think that the EST could have benefitted from a larger button that isn’t so delicate- I was so caught up with the other aspects of the EST (mainly its form and software) that after wiring the physical components (the button and LEDs) my focus completed shifted away from them. In retrospect, this was an error on my end and really limits the usefulness of my EST.
Ultimately I found that creating the physical form of my EST was by far the most challenging task for me. This honestly caught me off-guard, as a step I thought would take no more than a day suddenly required most of my concern and attention. I had anticipated the wiring and programming of the EST to be relatively straightforward, and thankfully that was indeed the case, but my estimate of how long I’d take on the physical aspect of the project was totally off and was a source of most of my issues. I regret aiming for such a tiny and intricate physical form of the EST from the beginning, and I wish I had instead started off with a simple laser-cut box as a prototype and then making it even smaller if time permitted. My desire to leverage 3D-printing ended up backfiring, and if I were to do things differently I would instead have learned how to laser cut for this project.
One of the few positives from the current version of the EST is that it works and can be improved much, much further. I can see myself building another iteration of this project that’s much smaller and more visually appealing. Given that I would have much more time (since I wouldn’t try another iteration otherwise), I would definitely attempt to reduce the EST to its smallest possible size.
/* Project 2 - Excercise Set Counter (EST) * Juan Meza (jmezavin) * * Description: * This code handles the turning on (and off) of the EST's LEDs. * The EST must appropriately respond to a button press, and this code * enables this. After reaching five sets and five lit-up LEDs, for instance, * the code resets them all to prepare for the next excercise. * * * Pin Mapping: * * Pin / Mode / Description * -------/---------/------------ * 8 input button * 9 output LED red 1 * 10 output LED red 2 * 11 output LED red 3 * 12 output LED red 4 * 13 output LED red 5 * * * Much thanks to the 'Task Buddy' project from the previous semester (F21) * for insight on how to handle button presses and timing! * https://courses.ideate.cmu.edu/60-223/f2021/work/task-buddy/ * */ #define BUTTON 8 #define LED1 9 #define LED2 10 #define LED3 11 #define LED4 12 #define LED5 13 bool isButtonPressed = false; unsigned long buttonTime; int ButtonPresses = 0; void setup(){ pinMode(BUTTON,INPUT_PULLUP); pinMode(LED1,OUTPUT); pinMode(LED2,OUTPUT); pinMode(LED3,OUTPUT); pinMode(LED4,OUTPUT); pinMode(LED5,OUTPUT); buttonTime = millis(); Serial.begin(9600); } void loop(){ int buttonVal = digitalRead(BUTTON); //Serial.println(buttonVal); if(buttonVal == 0 && millis() - buttonTime >= 500){ //button is being pressed for reasonable time //Serial.println(millis()-buttonTime); ButtonPresses++; buttonTime = millis(); //reset buttonTime for next button press //Serial.println("Button is being pressed."); Serial.println("Button has been pressed this many times:"); Serial.println(ButtonPresses); } else{ //Serial.println("Button is NOT being pressed."); } if(ButtonPresses >= 1){ digitalWrite(LED1,HIGH); //on, LOW for off } if(ButtonPresses >= 2){ digitalWrite(LED2,HIGH); } if(ButtonPresses >= 3){ digitalWrite(LED3,HIGH); } if(ButtonPresses >= 4){ digitalWrite(LED4,HIGH); } if(ButtonPresses == 5){ digitalWrite(LED5,HIGH); } if(ButtonPresses > 5){ //more than 5, reset! Serial.println("TIME TO RESET"); digitalWrite(LED1,LOW); digitalWrite(LED2,LOW); digitalWrite(LED3,LOW); digitalWrite(LED4,LOW); digitalWrite(LED5,LOW); delay(300); //do some blinking first digitalWrite(LED1,HIGH); digitalWrite(LED2,HIGH); digitalWrite(LED3,HIGH); digitalWrite(LED4,HIGH); digitalWrite(LED5,HIGH); delay(300); digitalWrite(LED1,LOW); digitalWrite(LED2,LOW); digitalWrite(LED3,LOW); digitalWrite(LED4,LOW); digitalWrite(LED5,LOW); delay(300); digitalWrite(LED1,HIGH); digitalWrite(LED2,HIGH); digitalWrite(LED3,HIGH); digitalWrite(LED4,HIGH); digitalWrite(LED5,HIGH); delay(300); digitalWrite(LED1,LOW); digitalWrite(LED2,LOW); digitalWrite(LED3,LOW); digitalWrite(LED4,LOW); digitalWrite(LED5,LOW); ButtonPresses = 0; } }
]]>
Math Buddy
Math Buddy is my new electronic friend who always wants to improve my math by asking me mathematical questions and expecting my responses. If I answer correctly, it will be so happy, show me a smiling face and raise its “correct” hand; when I do it wrong, it’ll ask me to try again (and again) until I figure out a correct solution.
Math Buddy – Rear View
Smiling Face with “Correct” Hand
Sad Face with “Wrong” Hand
At the initial stage of creating Math Buddy, I wanted to endow my little friend with some human-like interactive features but not simply ask questions and check my answers. There is a trade on between making things “lively” but keeping a simple (or clear) electronic logic and looking, so I chose to only integrate “facial expression” into the LCD screen and add “body language” which could be achieved by servo motors.
Testing Keypad input and LCD
Putting Electronics Together
After finishing all of the electronic and programming parts, I realized that my Math Buddy could be willing to have a fancy looking, which might make it more confident when it is standing in front of our classmates and its “Project 2” mates. At that moment, I recalled that when I was around 10, I had a Nintendo GameBoy Color with a purple-transparent case; I loved that machine not only because of the games but also I could always look at the tidy-designed electronic things inside. So I decided to make full use of my design knowledge to create a transparent case for my little buddy.
Container Design and Laser Cutting file Prep.
Assembling the Machine
Organizing Wires
One of the comment says “moving the hand with a checkmark to the right could be a better because of social customs. And also could paint the right one as green, the wrong one as red.” I think this is a great comment and I strongly agree with that because I didn’t really think about the user experience when designing the machine. “Binary coding the LEDs to make 8 levels” is such an interesting idea that I can have 8 levels with only 4 LEDs, as I was torn between doing more levels and reducing the number of LEDs (too many LEDs look messy).
From my side, I love my Math Buddy but I realize there are still some problems inside it. The case looks pretty good to me, but the input part is too large that wastes both space and material. There might be one solution that I could integrate a battery into those empty spaces to make my little machine self-powered. Reducing the size around the keypad might be another solution but that makes the machine look so wired; this is a view from the design aspect. I’m kind of not happy with the math question generation part of my code because I oversimplified the difficulty to “the number of digits”; I made a logic of more numbers in an equation means more difficult the question is. Another defect of the question part is that, there is a half chance I will get a “zero” result when generating subdivision questions; I might consider adding decimal features into the answer.
This project is a great chance for me to synthesize my design, programming, electronic knowledge together; I created a digital system with electronical and physical feedback. Also, I make full use of the pins on the Arduino Uno, so I might not have more pins to add more components if I keep using just one board. Overall, I’m very happy with the Math Buddy I made. It’s so much fun creating it and playing with it.
I actually planing to create another electronic buddy that can generate different types of questions rather than just math. With that buddy, the way I answer its question won’t be limited to typing the keypad, but also different types of sensors. But it is a general idea now; I haven’t gotten into detail about this proposal.
System Diagram
Electronic Schematic
/* Math Buddy Tingsong (Terrence) Ou Project Description: My dear Math Buddy randomly generates a math question with types of plus, minus, multiplication, and division, for me to answer. I can input my answer with a number keypad on which number means number, star sign means backspace, and hashtag means confirm. If I provided a correct answer, a greeting screen shows up, and a hand with checkmark raises, and the next question shows on the screen; if I did it wrong, a sad screen and a hand with a cross mark would remind me to try again. I won't see a new question until I solve the current one. There is a rank system controlling the difficulty; The more correct answer I responded, the harder the next question gonna be. Three LEDs represent difficulty visually: green (Lv.0) is easy, yellow (Lv.1) indicates intermediate, while red (Lv.2) means hard. A simple/easy question looks like 26 + 73 = ?. An intermediate question looks like 792 - 432 = ?. A hard question looks like 57 * 23 = ?. In the subtraction function, I wrote a while loop to ensure the first number is always larger than the second one to avoid a negative result. Pin mapping: Arduino pin | role | description ------------|--------|---------------- 2 input Keypad (COL0) 3 input Keypad (COL1) A2 input Keypad (COL2) A4 input Keypad (ROW0) A0 input Keypad (ROW1) A1 input Keypad (ROW2) A3 input Keypad (ROW3) SDA output LCD SDA SCL output LCD SCL 7 output LED (Green) 8 output LED (Yellow) 9 output LED (Red) 10 output Servo 11 output Servo Reference: LCD screen control: https://courses.ideate.cmu.edu/60-223/s2022/tutorials/I2C-lcd Keypad: https://arduinogetstarted.com/tutorials/arduino-keypad */ #include <Keypad.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Servo.h> //----------------------- Keypad Initializer -------------------- //Keypad setup; Because the keypad is "flipped" compare to the regular ones, I flipped the matrix vertically const byte ROWS = 4; const byte COLS = 3; char keys[ROWS][COLS] = { {'E', '0', 'D'}, {'9', '8', '7'}, {'6', '5', '4'}, {'3', '2', '1'} }; byte rowPins[ROWS] = {A4, A0, A1, A3}; byte colPins[COLS] = {2, 3, A2}; //PIN A0, A1, A2, A3, 2, A4, 3 reserved for keypad; they're not in sequence but easier for me to organize physical connections Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //----------------------- LCD, LED and Servo Initializer -------------------- //I'm using a 20 * 4 LCD in this project LiquidCrystal_I2C screen(0x27, 20, 4); Servo correctHand, wrongHand; //PIN 10, 11 reserved for servos int correctPin = 10, //For the hand with check mark wrongPin = 11, //For the hand with cross mark greenLED = 7, yellowLED = 8, redLED = 9; //----------------------- Math-related Parameters -------------------- String question = ""; String answer = ""; String correctAnswer = ""; int numA = 0, numB = 0, correctNum = 0, wrongNum = 0, level = 0, prevLevel = 0; //----------------------- Main Program -------------------- void setup() { screen.init(); screen.backlight(); randomSeed(analogRead(0)); //Initializing LEDs pinMode(greenLED, OUTPUT); pinMode(yellowLED, OUTPUT); pinMode(redLED, OUTPUT); //Initialize Question Screen generateQuestion(); showQuestion(question, answer); //Initialize Servos correctHand.attach(correctPin); wrongHand.attach(wrongPin); correctHand.write(10); wrongHand.write(170); } void loop() { controlLight(); //Controling The LED light based on current level readKey(); // Read input from numpad } /* ####################### HELPER FUNCTIONS ###################### */ //-----------------------LCD Control Stack--------------------- //Displaying the question and input answer on the LCD void showQuestion(String question, String answer) { /* String question: a math question in the format of String String answer: the input answer in the format of String */ //Print question and input answer screen.home(); screen.setCursor(0, 0); screen.print("Question:"); screen.setCursor(0, 1); screen.print(question); screen.setCursor(0, 2); screen.print("Your Answer:"); screen.setCursor(0, 3); screen.print(answer); //Print lurrent level screen.setCursor(15, 0); screen.print("Lv." + String(level)); //Print the number of correct and incorrect answers screen.setCursor(16, 1); screen.print("O:" + String(correctNum)); //Correct Answer screen.setCursor(16, 2); screen.print("X:" + String(wrongNum)); //Wrong Answer } //Removing last digit typed (backspace) void clearLastInput() { int lastIdx = answer.length() - 1; screen.setCursor(lastIdx, 3); screen.print(" "); } //Removing the question from LCD void clearQuestionLine() { for (int i = 0; i < 20; i++) { screen.setCursor(i, 1); screen.print(" "); } } //Removing the input answer from LCD void clearAnswerLine() { for (int i = 0; i < 20; i++) { screen.setCursor(i, 3); screen.print(" "); } } //---------------------Math Question Stack------------------ //Generating math questions void generateQuestion() { int mode = random(0, 4);//0-plus, 1-minus, 2-mult, 3-divid int pmBound = 100; //plus,minus bounds int mdBound = 10; //multiplication,division bounds if (level > 0) { pmBound = 1000; } if (level > 1) { mdBound = 100; } switch (mode) { case 0: numA = random(1, pmBound); numB = random(1, pmBound); correctAnswer = String(numA + numB); question = String(String(numA) + " + " + String(numB) + " = ?"); break; case 1: numA = random(1, pmBound); numB = random(1, pmBound); while (numB > numA) { numA = random(1, pmBound); numB = random(1, pmBound); } correctAnswer = String(numA - numB); question = String(String(numA) + " - " + String(numB) + " = ?"); break; case 2: numA = random(1, 100); numB = random(1, mdBound); correctAnswer = String(numA * numB); question = String(String(numA) + " * " + String(numB) + " = ?"); break; case 3: numA = random(1, 100); numB = random(1, mdBound); correctAnswer = String(int(numA / numB)); question = String(String(numA) + " / " + String(numB) + " = ?"); break; } } //Checking current level void checkLevel() { int diff = correctNum - wrongNum; if (diff >= 10) level = 2; else if (diff >= 5) level = 1; else level = 0; prevLevel = level; } //---------------------Input Control Stack------------------ //Read Key void readKey() { char key = keypad.getKey(); //If key pressed (not * or #) and the answer length is smaller than 20, add current input to the answer screen if (key != NO_KEY && key != 'D' && key != 'E' && answer.length() < 20) { answer += key; showQuestion(question, answer); } //D(*) Remove last input digit; E(#) Check answer if (key == 'D') { clearLastInput(); answer.remove(answer.length() - 1); //The sequence of these two lines are very important showQuestion(question, answer); } else if (key == 'E') { if (answer == correctAnswer) { correctNum += 1; //If correct, show greeting screen and raise correct hand greetScreen(); correctHand.write(170); delay(1500); correctHand.write(10); screen.clear(); generateQuestion(); } else { sadScreen(); wrongNum += 1; wrongHand.write(10); delay(1500); wrongHand.write(170); screen.clear(); } checkLevel(); answer = ""; //Reset answer to empty string clearAnswerLine(); showQuestion(question, answer); } } //---------------------Output Control Stack------------------ //Controlling the LED light accoring to current level void controlLight() { digitalWrite(greenLED, LOW); digitalWrite(yellowLED, LOW); digitalWrite(redLED, LOW); if (level == 0) digitalWrite(greenLED, HIGH); if (level == 1) digitalWrite(yellowLED, HIGH); if (level == 2) digitalWrite(redLED, HIGH); } //Printing greeting screen when the result is correct void greetScreen() { byte smileEye[] = { B00100, B01010, B10001, B00000, B00000, B00000, B00000, B00000 }; byte smileMouth[] = { B00000, B00000, B00000, B00000, B10001, B01110, B00000, B00000 }; byte hand[] = { B00000, B00000, B01110, B10001, B00001, B00010, B01100, B00000 }; screen.createChar(0, smileEye); screen.createChar(1, smileMouth); screen.createChar(2, hand); screen.clear(); screen.setCursor(6, 1); screen.print("("); screen.setCursor(7, 1); screen.write(2); screen.setCursor(8, 1); screen.write(0); screen.setCursor(9, 1); screen.write(1); screen.setCursor(10, 1); screen.write(0); screen.setCursor(11, 1); screen.print(")"); screen.setCursor(12, 1); screen.write(2); screen.setCursor(5, 2); screen.print("GOOD JOB!!"); } //Printing "try again" screen void sadScreen() { byte leftEye[] = { B00001, B00110, B01000, B10000, B00110, B00110, B00000, B00000 }; byte rightEye[] = { B10000, B01100, B00010, B00001, B01100, B01100, B00000, B00000 }; byte sadMouth[] = { B00000, B00000, B00000, B00000, B00100, B01010, B10001, B00000 }; screen.createChar(3, leftEye); screen.createChar(4, sadMouth); screen.createChar(5, rightEye); screen.clear(); screen.setCursor(7, 1); screen.print("("); screen.setCursor(8, 1); screen.write(3); screen.setCursor(9, 1); screen.write(4); screen.setCursor(10, 1); screen.write(5); screen.setCursor(11, 1); screen.print(")"); screen.setCursor(12, 1); screen.write(2); screen.setCursor(5, 2); screen.print("TRY AGAIN!!"); }
]]>
Overall Photo
The candy funnel seen without candy.
The candy funnel seen with candy.
Internal Wiring
Above is my initial design idea of the Good Habits Checklist. In the initial design, candy was stored in a compartment and once all 5 switches were flipped, a servo would turn a latch, allowing me to open the compartment. Another thing to note was that the display and switches were on the side of the box, which is different from my final design.
Above shows my first prototype. As you can see, I focused on getting the wiring and display before moving on to the exterior box. I switched the number of tasks from 5 to 4 to fit onto the display effectively but did not change much of the design concept at this point.
Shown above is when I began to fit all of my components into the box. This is where the initial design changed the most. After presenting my prototype, other students gave me the idea to create a dispensing mechanism for the candy. I was still able to use the servo that releases candy from a funnel by turning out of the way for a short period of time. Getting the correct timing to dispense a reasonable amount of candy was difficult. Having this new mechanism also allowed me to put the display, switches and LEDs on top of the box. Because I am tall and tower above most tables even when sitting, this allows me to more easily see the display and flip the switches.
Overall, I believe that my final critique went well and that I achieved what I set out to do. It was very rewarding to get positive feedback about my candy dispensing system. One student commented that, “I think the candy dispensing is exciting and looks like it functions really well.” Although creating a candy dispenser was not my idea, as I explained in the Process Images section, I thought my idea of the funnel and servo motor was a clever solution. I do, however, wish it was a little more consistent with the number of skittles that come out. Right now, it usually dispensing around 3 skittles but can dispense 1 to 5. Furthermore, students commented how the skittles are launched out onto the desk and can go very far. One student advised me to, “Add a holder for the treats once they’re dispensed.” This is a great idea because it ensures that skittles do not go onto the floor or get dirty from the table. In the end, I truly believe that this will be a useful thing for me to use every day and develop good habits. I am happy with the design and function of the project, but I still plan to fix all the issues explained above. I plan to go back and add a candy holder, tweak the dispenser so it is more consistent, and put an on/off switch so that the batteries do not run out as fast. Then I can get the most out of using the Good Habits Checklist.
Block Diagram
Schematic Diagram
/* The Good Habits Checklist Tristan Hineman Description: This program is designed to display daily tasks and read if LEDs are on or off. When an LED is on, the LCD Display will show that the correlated task is completed. When it reads that all LEDs are on, it displays that all tasks are done and turns a servo motor to release candy once. Pin Mapping Table: Arduino pin | description ------------|------------- A0 Servo Motor 5 LED 1 6 LED 2 7 LED 3 8 LED 4 SDA SDA pin, LCD Display SCL SCL pin, LCD Display */ //Libraries #include <LiquidCrystal_I2C.h> #include <Servo.h> Servo servoMotor; //Setup to LCD display LiquidCrystal_I2C screen(0x27, 20, 4); //Creates and saves a special character that is a check mark byte customChar[] = { B00000, B00000, B00001, B00011, B10110, B11100, B01000, B00000 }; //Pin for LEDs const int led1Pin = 5; const int led2Pin = 6; const int led3Pin = 7; const int led4Pin = 8; //Pin for servo motor const int servoPin = A0; //Variable to run servo motor only once when all LEDs are on boolean hasRun = false; void setup() { //Pin setup pinMode(servoPin, OUTPUT); pinMode(led1Pin, INPUT); pinMode(led2Pin, INPUT); pinMode(led3Pin, INPUT); pinMode(led4Pin, INPUT); //LCD screen initialization screen.init(); screen.backlight(); screen.home(); Serial.begin(9600); //Variable for check mark screen.createChar(check, customChar); } void loop() { //Attaching library to servo pin servoMotor.attach(servoPin); //Variable to set servo position int servoPos; //Variables to read if LEDs are on int led1Read; int led2Read; int led3Read; int led4Read; //Read and print if LEDs are on led1Read = digitalRead(led1Pin) ; Serial.print(led1Read); led2Read = digitalRead(led2Pin) ; Serial.print(led2Read); led3Read = digitalRead(led3Pin) ; Serial.print(led3Read); led4Read = digitalRead(led4Pin) ; Serial.println(led4Pin); //Displaying text when all LEDs are on if (led1Read == 0 && led2Read == 0 && led3Read == 0 && led4Read == 0) { screen.setCursor(0, 0); screen.print("ALL TASKS DONE! "); screen.setCursor(0, 1); screen.print("ALL TASKS DONE!"); screen.setCursor(0, 2); screen.print("ALL TASKS DONE!"); screen.setCursor(0, 3); screen.print("ALL TASKS DONE!"); //Using hasRun variable to move the servo motor only once if (hasRun == false) { servoMotor.write(90); delay (100); servoMotor.write(180); hasRun = true; } } else { hasRun= false; servoMotor.write(180); //Displaying the task and an X when the LED 1 is off if (led1Read == 1) { screen.setCursor(0, 0); screen.print("1)Eat Breakfast:X"); } //Displaying the task and a check mark when the LED 1 is on else { screen.setCursor(0, 0); screen.print("1)Eat Breakfast:"); screen.write(check); } //Displaying the task and an X when the LED 2 is off if (led2Read == 1) { screen.setCursor(0, 1); screen.print("2)Exersize:X "); } //Displaying the task and a check mark when the LED 2 is on else { screen.setCursor(0, 1); screen.print("2)Exersize: "); screen.setCursor(11, 1); screen.write(check); } //Displaying the task and an X when the LED 3 is off if (led3Read == 1) { screen.setCursor(0, 2); screen.print("3)Homework:X "); } //Displaying the task and a check mark when the LED 3 is on else { screen.setCursor(0, 2); screen.print("3)Homework: "); screen.setCursor(11, 2); screen.write(check); } //Displaying the task and an X when the LED 4 is off if (led4Read == 1) { screen.setCursor(0, 3); screen.print("4)Brush Teeth:X"); } //Displaying the task and a check mark when the LED 4 is on else { screen.setCursor(0, 3); screen.print("4)Brush Teeth:"); screen.write(check); } } }
]]>
Smart Hat with Rotatable Brim
This smart hat can detect where the brightest light is (usually the sunlight), and rotate the brim to face that area- keeping you always in the shade.
Final Project Photos
Final product with all components placed together.
Hat component contains velcro for mechanism attachment.
Stand-alone mechanism that sits on top of the hat.
Photoresistors are divided with cardboard to improve light detection accuracy.
Crushed and cut aluminum foil tubing was used as a support for the brim.
Video Demonstration
Process Review
One key decision point was deciding how to detect the brightest light. There were many ideas here, mostly focused on where and how to arrange the photoresistors. To detect the brightest light, multiple photoresistors would be needed for comparison purposes. A ring of photoresistors attached around the hat (and as such, would be stationary) would offer a way to maintain a “grounded” reference, and as such, one would simply need to turn the motor to the position of the photoresistor with the brightest light detected. This idea was turned down due to the fact that it would require many wires running along the hat, resulting in hat discomfort, but also the fact that when the brim turned to the correct position, it would shade the photoresistor (and making it not detect the brightest light). This latter problem would cause a logical flaw, as it would therefore be impossibly to maintain the hat over the same spot, since the brightest spot would always end up shaded. Thus, the solution had to be that the photoresistors were located above the brim attachment, and that means they had to rotate as well. Putting a ring on top of the motor would work, but in effect, a smarter approach could be used; an entire ring would not be necessary, since we simply need a comparison of brightness. For example, as little as two photoresistors could work, since as long as one is brighter than the other, then we can focus on what direction the motor needed to move to, and not exactly what position it needed to move to. As such, only an arc of photoresistors would be needed, though it is needed to note that having too few photoresistors could lead to inaccuracy, due to low amounts of data collection. Thus, the final product used four photoresistors, employing the smarter approach without sacrificing too much accuracy.
These were initial plans and sketches as to what orientation of photoresistors would be best.
Another key decision point was what motor to use to move the brim. The original approach was with a stepper motor, since it would allow for the most precise 360-degree controlled movement. However, after wiring and playing around with it for a bit, two obvious problems stood out: 1) the physical size and 2) the power source. The stepper motor was incredibly large and heavy, to the point where it could be unreasonable to belong on a head. Due to its hefty proportions and abilities, it also required a 12V power supply- which required an entire extra battery pack. Upon further consideration, I switched to the DC motor for the final product. A DC motor solved both problems- it was smaller and lighter, and it only needed a 6V power supply. The 6V power supply was crucial, since giving it 5V would be sufficient, and that meant it was possible to unify a power supply with the Arduino, who also used a 5V power supply. While DC motors would not be able to control exact position, this can be resolved using the smart method as aforementioned in the previous decision point- exact position does not matter; only direction does. Fortunately, changing the direction of a DC motor was incredibly simple; all that needed to be done was flipping the power and ground voltages of the two pins of the DC motor. The only potential issue was that DC motors had slightly less torque than a stepper motor, but after some testing, this proved to be negligible.
The large size of the stepper motor and extra battery pack caused orientating the mechanism to be difficult and overly bulky.
Process Images
This is the first prototype of the mechanism- with stepper motor and large, extra battery pack.
This is an in-depth look at the circuitry involved, primarily focusing on the photoresistors and accompanying resistors.
This is initial experimentation as to what material to make the brim support out of.
Initial bundling of the electronics together showed promise of a small mechanism.
Everything in the mechanism of the final version is combined- electronics, DC motor, and brim.
Adding a cardboard plating to the inside of the hat was one of many attempts to prevent the mechanism from tilting.
Discussion and Reflection
In general, this project proved to be quite a challenge, and as a result, was good, but not completely perfect. On the bright side (pun not intended), the overall mechanism of the hat worked- the motor rotated the brim towards the brightest light that was detected by the photoresistors. Coding had been relatively straightforward, especially due to the smart method of photoresistor tracking; all that was needed was several comparisons and changing the voltages as necessary.
What failed was the very last step, when everything had to come together. The mechanism proved to be too unbalanced to be placed on the hat. While I had devised six different plans to combat this should it occur, none of them ended up working due to one fatal flaw: the roundness of my head. The original plan was that the hat portion would firmly be on the head, and the mechanism would just be attached to the top. The unfortunate truth was that the mechanism was simply too top-heavy, and the long axle coming out of the motor had a small pivot surface. As such, several attempts to create a wider base for the long axle were made- however, any structure would have a flat surface, and that meant that it would tilt on my head’s roundness. Other attempts to use strong adhesives, like velcro and hot glue, to forcefully and stiffly attach it to the hat did not fully work; while the mechanism was firmly attached to the hat, it would still tilt due to the same original reasons. Cardboard/wood underlays were placed under the cap to try and create a flatter surface on the head, but these did not work, since the underlays themselves would tilt due to my head’s roundness. I thought shifting the location of where the mechanism would be attached to the hat, with similar regards to the comment that I should “add the velcro on the hat to the top center so its on the most “flat” surface of [my] head,” would work, but it turns out no area of my head was flat enough- there was always tilting.
In retrospect, I should have tried more plans, and earlier as well; it was just particularly unfortunate that it was the last step where everything failed. In general, what I managed to do successfully had gone rather smoothly- everything that I struggled in was due to a lack of knowledge. For example, this project affirmed to me that coding-wise, I should not have much problems; while Arduino does have new syntax I still need to learn, it should not be a major issue. I learned about motor differences and usages, how to use AutoCad, etc. There was much to learn, and still is much to learn. The best way to improve my skills is to constantly keep asking for advice on how to approach tasks, and learn as many new techniques as possible. All in all, if anything, learning that I did not learn enough to make this project work completely was a lesson that was valuably learned.
Despite my shortcomings during this project, I do not plan on giving up on making the final product work as intended. I have wanted to build something like this for many years, and it would be silly, after all the advice and information I have learned, to end the project in its current state. I plan to further inquire for assistance regarding their advice, while proceeding further with some ideas of my own. For example, an initial naive idea would be to carve a piece of wood to fit my head’s roundness, while also providing a flat surface on the other side. I also particularly took interest in a comment that I could “wield the [motor’s] rod into an iron helmet,” since an iron helmet (or more probably some metal skeleton) would eliminate the need for a flat surface (they would be conjoined) and still have the shape for my head. Whatever the final changes may be, I plan to complete this project, and come to school wearing it in all of its hilarious and quirky glory.
Functional Block Diagram and Schematic
Project Code
/* Project Title: Smart Hat with Rotatable Brim * Creator: David Wu * * This code takes in the input of four photoresistors to * find the brightest light source, and then, if needed, * rotates the brim of the hat in that general direction * by flip-flopping the voltages on the pins of the DC motor. * The brim is centered between the second and third * photoresistors. The code is adaptable to left-right * orientation (physical implementation of the four photoresistors * does not matter as it just requires re-defining which * direction the brim will turn). * * Pin Mapping: * Photoresistor 1 : A0 * Photoresistor 2 : A1 * Photoresistor 3 : A2 * Photoresistor 4 : A3 * DC Pin 1 : 5 * DC Pin 2 : 6 * */ const int DCPIN1 = 5; // DRV8833 B1 in const int DCPIN2 = 6; // DRV8833 B2 in const int PHOTOPIN1 = A0;// photoresistor input const int PHOTOPIN2 = A1;// photoresistor input const int PHOTOPIN3 = A2;// photoresistor input const int PHOTOPIN4 = A3;// photoresistor input int timer = 0; // to slow down motor responsiveness void setup() { pinMode(PHOTOPIN1, INPUT); pinMode(PHOTOPIN2, INPUT); pinMode(PHOTOPIN3, INPUT); pinMode(PHOTOPIN4, INPUT); pinMode(DCPIN1, OUTPUT); pinMode(DCPIN2, OUTPUT); Serial.begin(9600); } void loop() { // prevent motor from switching too quickly and minimize jittering if(millis() - timer >= 500) { // read photoresistor values (0 - 1023) int bright1 = analogRead(PHOTOPIN1); int bright2 = analogRead(PHOTOPIN2); int bright3 = analogRead(PHOTOPIN3); int bright4 = analogRead(PHOTOPIN4); // calculate the brightest spot int max12 = max(bright1, bright2); int max34 = max(bright3, bright4); int maxBright = max(max12, max34); // if brightest is at center (where brim is) if((bright2 == maxBright) && (bright3 == maxBright)){ // don't move the motor analogWrite(DCPIN1, 0); analogWrite(DCPIN2, 0); } // if brightest is to the left else if ((bright1 == maxBright) || (bright2 == maxBright)){ // rotate brim to the left analogWrite(DCPIN1, 0); analogWrite(DCPIN2, 128); } // if brightest is to the right else{ // rotate brim to the right analogWrite(DCPIN1, 128); analogWrite(DCPIN2, 0); } timer = millis(); } }
]]>
Overall image
servo latch detail
I made some initial ideation sketches based on problems I had daily. (include sketches) I settled on creating a prize box for myself since I often found myself lacking the motivation to finish tasks, eat well, or exercise. I was also inspired by some projects from previous years where they used a combination of switches and LEDs to create tactile and visual interaction. The initial idea was to create a box-shaped device with switches and LEDs. Each switch represented something I had to do and as each switch gets flipped to signify completing a task, the corresponding LED will light up. Once all the switches are flipped, a servo motor would rotate to open a lid to reveal a prize I left for myself beforehand (so I’m rewarding myself).
Initial idea sketches
Since I had quite a lot of individual components (5 switches, 5 LEDs, and a servo motor), I wanted to make sure there were enough plugs on my Arduino by first wiring everything in draw.io. I also had experience working with these components before so I was able to get the wiring and code done relatively quickly. However, I had significant difficulty with the physical fabrication of my device.
For my prototype, I used cardboard to make a box with the LEDs and switches in their own rows. But I was having trouble figuring out how to place the servo so that it could open the lid vertically. Instead, I could only position the motor to rotate the lid horizontally. So during the prototype crit, someone suggested: “I think that you can open it vertically by placing the servo motor parallel to the ground. It would be the servo rotate vertically”. I thought that was a good idea so I incorporated that into my design with the motor in the back of the box moving like a hinge to open the door
Since I had most of my wiring and code working by prototype crit, I wanted to challenge myself a little more. For the few days prior, I had been forgetting to bring things like my ID, AirPods, and water bottle with me in the morning. So I changed my concept to be a device that reminds me to check that I have all my things before I leave my house. Using the same base, I added 2 additional components: an ultrasonic ranger and buzzer. The device will therefore be mounted on my door so when I pass it in the morning it will beep and remind me to check it. Each switch corresponds to an item I need to bring, which I will flip if I have it in my bag. Instead of it being a prize, it will instead be my ID that will drop down. Once I have my ID, I can leave my house.
Updated idea sketch
wiring of all the components
After adding the additional components, I started 3D modeling my device in Solidworks to 3D print, making sure to leave holes in some areas to panel-mount the different electronic components. But after talking to Zach, I realized 3D printing takes a long time and some holes on my box faces may ruin the dimensions needed to fit edges together and fit switches through. So I laser-cut pieces instead for a finger-joint box and welded them together.
soldering switch wires
initial 3D model for 3D print
Lasercut pieces
plastic welding pieces together
After I finished putting together the box, I had a problem with the servo again where the arm wasn’t long enough to fully support the lid and couldn’t hold it in place against gravity.
Instead of working against gravity, I thought to work with it and changed the servo position so instead of acting as a hinge to open the bottom door, it is more of a latch. When the arm rotates away, the lid naturally falls open so the ID can drop down.
During the final crit, I got a couple feedback points on how to improve my device. Someone said “If you wanted to go further, you could maybe add a motor to open/close the door, and not just rely on gravity” which was my original goal and is something I’d still like to achieve. I think I will need a more secure and longer arm to support the lid. The guest critiquer also said that the motor makes the device look slightly unfinished and suggested using magnets that work only when electricity is running through it to lock and unlock the lid. I also think this is a viable idea and probably something I would have tried too if I had more time. Someone else also pointed out “The gravity might lead the key to drop to the ground; is it possible to place a basket below the machine to avoid the key from going all the way down?” This is something I actually forgot to consider and I think is a great suggestion. I will probably make another tray to be mounted below the device that will catch the ID if it misses the user’s hand.
Overall, I’m quite happy with my project since it is pretty similar to my concept and it works well too. While I think the aesthetic design can be improved slightly, such as making it less box-shaped and making it smaller, I’m proud of what I was able to make with my minimal Arduino skills. It was also an exciting process since I’ve never designed and made something with electronics before even though it was something I’ve always wanted to try and incorporate into my work as a design student. Compared to the previous project where my partner and I struggled with coding the parts to make them work together, I was surprised that the wiring and coding were relatively straightforward for me and how well the components were working. I’d like to think it shows some improvement between the 2 projects and how I’ve learned from my mistakes before.
However, I was still slightly disappointed in the physical aspect of it since I had more details in mind but I couldn’t incorporate with the material and form. If I had more time, I would have probably prototyped more and experimented with different materials. I’d also try changing it from being too box-shaped and looking at possibilities for softer edges and possibly rounded designs. For that, I’d also try 3D printing my device to achieve those details. While not everything went according to plan, I think I responded well and was flexible with changing my idea as needed, and still made a project I can proudly present.
/* "Don't Forget Your Things!" * * Catherine Liu * * This code is for a device that reminds you to take your things with you in the morning. Each switch is * wired to an LED and a servo motor rotates when all switches are flipped * * Pin Mapping: * pin | mode | description * ----|--------|------------- * 2 . output . servo motor * 3 . output . LED 1 * 4 . output . LED 2 * 5 . output . LED 3 * 6 . output . LED 4 * 7 . input . ultrasonic ranger trigger pin * 8 . input . switch 1 * 9 . input . switch 2 * 10 . input . switch 3 * 11 . input . switch 4 * 12 . input . ultrasonic ranger echo pin * 13 . output . buzzer * re * Code Used: * Robert Zacharias: Displaying data on an I²C LCD screen (https://courses.ideate.cmu.edu/60-223/s2022/tutorials/I2C-lcd) */ #include <NewPing.h> #include <Servo.h> #include <LiquidCrystal_I2C.h> //LCD Display Module library LiquidCrystal_I2C screen(0x27, 16, 2); //initiate screen //LED pins const int LEDPIN1 = 3; const int LEDPIN2 = 4; const int LEDPIN3 = 5; const int LEDPIN4 = 6; //switch pins const int SWITCHPIN1 = 8; const int SWITCHPIN2 = 9; const int SWITCHPIN3 = 10; const int SWITCHPIN4 = 11; int switchCount = 0; // //buzzer pin const int BUZZERPIN = 13; bool buzzerSound = false; int buzzerCount = 0; //servo pin Servo lidMotor; const int MOTORPIN = 2; //distance sensor pin #define TRIGGERPIN 7 #define ECHOPIN 12 #define MAXDISTANCE 200 NewPing sonar(TRIGGERPIN, ECHOPIN, MAXDISTANCE); // NewPing setup of pins and maximum distance. //boolean for resetting the device bool switchReset = false; void setup() { Serial.begin(115200); pinMode(LEDPIN1, OUTPUT); pinMode(LEDPIN2, OUTPUT); pinMode(LEDPIN3, OUTPUT); pinMode(LEDPIN4, OUTPUT); pinMode(SWITCHPIN1, INPUT); pinMode(SWITCHPIN2, INPUT); pinMode(SWITCHPIN3, INPUT); pinMode(SWITCHPIN4, INPUT); pinMode(BUZZERPIN, OUTPUT); lidMotor.attach(MOTORPIN); lidMotor.write(5); screen.init(); screen.backlight(); screen.home(); screen.print("Hi!Make sure you"); screen.setCursor(0,1); screen.print("have everything"); } void loop() { int buttonState1; int buttonState2; int buttonState3; int buttonState4; buttonState1 = digitalRead(SWITCHPIN1); buttonState2 = digitalRead(SWITCHPIN2); buttonState3 = digitalRead(SWITCHPIN3); buttonState4 = digitalRead(SWITCHPIN4); //checking distance delay(50); int distVal = sonar.ping_cm(); Serial.println(distVal); //buzzer sounds 3 times max if object is detected if ((distVal < 30) && (buzzerSound == false)) { digitalWrite(BUZZERPIN, HIGH); delay(100); digitalWrite(BUZZERPIN, LOW); buzzerCount = buzzerCount + 1; } if (buzzerCount == 3) { buzzerSound = true; } //switch 1 if (buttonState1 == LOW) { switchCount = switchCount + 1; digitalWrite(LEDPIN1, HIGH); } if (buttonState1 == HIGH) { digitalWrite(LEDPIN1, LOW); } //switch 2 if (buttonState2 == LOW) { digitalWrite(LEDPIN2, HIGH); } if (buttonState2 == HIGH) { digitalWrite(LEDPIN2, LOW); } //switch 3 if (buttonState3 == LOW) { digitalWrite(LEDPIN3, HIGH); } if (buttonState3 == HIGH) { digitalWrite(LEDPIN3, LOW); } //switch 4 if (buttonState4 == LOW) { digitalWrite(LEDPIN4, HIGH); } if (buttonState4 == HIGH) { digitalWrite(LEDPIN4, LOW); } //rotate motor, and change LCD text after all switches are flipped if ((buttonState1 == LOW) && (buttonState2 == LOW) && (buttonState3 == LOW) && (buttonState4 == LOW)) { screen.clear(); screen.home(); screen.print("Goodbye!"); lidMotor.write(170); switchReset = true; } //motor returns to relatch lid else { lidMotor.write(70); } //reset buzzer when switches are flipped back if ((buttonState1 == HIGH) && (buttonState2 == HIGH) && (buttonState3 == HIGH) && (buttonState4 == HIGH) && (switchReset == true)) { screen.home(); screen.print("Hi!Make sure you"); screen.setCursor(0,1); screen.print("have everything"); buzzerSound = false; switchReset = false; buzzerCount = 0; } }]]>
Here you can see all of the components of the machine, primarily being the main box with all of the electronic components as well as the spray bottle mechanism with the servo motor bridge
Here is a view of the inside circuitry, where you can mostly see the breadboard, which lies on top of the battery pack, and the hook up to the LCD display in the lid.
Here’s a view of how I would intend to use the machine, probably laying head down on my studio desk with the spray bottle over my head to spray me.
Above I show the earliest part of my design process: where I first came up with the idea for “The Anti-Snooze Machine”! At this point I had not yet decided how I was going to actually cause the spray bottle mechanism to spray water, but I was thinking of having it held down and then a servo motor hitting it to push the lever. However, because of the amount of error that could be incurred with finding the angle for that, instead I decided to use a pulling motion instead with some wire.
This is my project in the early stages of the design process, when I was making my prototype. At this point I had to decide how I was going to move through the functions of my alarm clock function. I decided that having three different buttons would be the easiest way for me to assign the different functionalities to them and also make the user interface not as dependent on the same hardware. I actually ended up adapting from a four button format to having a three button format, incorporating a “next” button to help me move through different functions with my code.
When facing the final assembly of my different parts, I wanted to create a clean exterior that wouldn’t be annoying to figure out for someone who is just waking up. Since I needed a solid backing on my buttons so they can be pressed fairly aggressively, I decided to keep them on the breadboard and instead use the casing to hold the wires down the sides. I found it to be more affected carving down into the box to create the opening to the buttons instead of bringing them up to the surface.
Some design decisions that I might have made in retrospect include getting a stronger servo or pulling mechanism so there was less uncertainty with the spray mechanism working. Furthermore, I wish I had a cleaner looking outside portion for the spray bottle instead of a more pieced-together hold for it.
During the final critique for this project, I feel like I had a few highs and a few points of improvement that could have been made. For instance, many people liked the design of my box, which held the power source, arduino, and other wiring components. Specifically, people said that they “love the design of the box” and that it was “really well thought out”. I was really happy to hear that the design of the final product was clean and well appreciated since I wasn’t able to get those parts laser cut as I had originally intended to do. On the other hand, it was pointed out in my feedback that for one person, “as a chronic snooze button presser, [they] could see [them]self getting used to the water spray” and that it would be beneficial to vary the consequences of hitting the snooze button. With this in mind, I would consider adding different levels of alarm, maybe changing the volume to increase as the snooze count increases. I would try incorporating more complex consequences, like launching a soft foam ball or something along those lines, but it was also pointed out to me that “it would be cool if the box and water spray bottle were incorporated into one form so it takes up less space on the desk”. The design is a little bulky as it is, so I would have to adjust the components to make it take up less space or avoid adding a lot of different parts. In light of the positive feedback and despite the negative feedback, I was overall very pleased with the way that my project came out; it was very mechanically demanding to get all of the parts assembled together and I think I did the best I could with putting them into one form. I think I could have been a little more consistent moving across the whole piece, making it out of the same material instead of changing the spray bottle mechanism material from pulp board to tape and popsicle sticks. All in all, I think I found that my strong points in the design and development process were the aesthetic appeal of the final product and getting through the software component of the design, but I think I was lacking when it came to the mechanical build and should have thought to compromise on the initial design that I had to create a more consistently functional final product. I think that the next time that I go through a similar process, I should be more flexible in getting to my final product and adjusting the ideas I had for the mechanical parts to overall work better. For this project in particular, I think if I were to create another iteration I would try out different ways of triggering the spray mechanism so that it functions more consistently.
//The Anti-Snooze Machine //Shreeja Harisrikanth //This code makes it so the arduino recieves transmission from the //buttons and is able to count the amount of time that has passed //and then tells the servo when to move and activate the spray bottle // //Pin A0 is the servo pin //Pin 3 is the buzzer pin //the time incrementing button is pin 6 //the next function button is pin 8 //the snooze button is pin 10 // //I used professor Robert Zacharias's code to set up my LCD display //Here I import the necessary libraries for implementing my other //functions and parts #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C screen(0x27, 16, 2); #include <Servo.h> //Here I establish the servo and buzzerpins, two components that are //recieving input instead of sending it to the arduino Servo servoMotor; const int SERVOPIN = A0; const int BUZZERPIN = 3; //This is the LCD code that I used from the given example to //program the writing it does byte frowny[] = { B00000, B11011, B11011, B00000, B00000, B01110, B10001, B00000 }; //Here I establisht the buttons that are being used, //including one that I later eliminated const int TIMEINCBUTTON = 6; const int NEXTBUTTON = 8; const int NEXTBUTTON2 = 4; const int SNOOZEBUTTON = 10; //These are the integers that I use to keep track of //the amount of time passed, current time, and number of //times that the snooze button has been hit int hourCount = 0; int minCount = 0; int snoozeCount = 1; unsigned long lastTime; //These are the set of boolean values that I rely on to //help me cycle through the different functions, such as //the time values being set and whether snooze has been hit //or not boolean hourSet = true; boolean minSet = false; boolean timerSet = false; boolean alarm = false; //In the setupo I establish each of the pins as inputs and ourputs //and also create the baseline values I want for each of my counts void setup() { int hourCount = 0; int minCount = 0; int snoozeCount = 1; unsigned long lastTime; boolean hourSet = true; boolean minSet = false; boolean timerSet = false; pinMode(TIMEINCBUTTON, INPUT); pinMode(NEXTBUTTON, INPUT); pinMode(NEXTBUTTON2, INPUT); pinMode(SNOOZEBUTTON, INPUT); pinMode(BUZZERPIN, OUTPUT); //Here I begin to display what the user would have as instructions //onto the LCD display screen Serial.begin(9600); Serial.println("click the time button to increase hours"); Serial.println("Click the next button to increase minutes"); screen.init(); screen.backlight(); screen.home(); //Here I set the servo baseline and serial servoMotor.attach(SERVOPIN); Serial.begin(9600); servoMotor.write(0); delay(1000); } void loop() { screen.print("increase hours"); //Depending on the boolean value that is given, there will be a //different set of functions that are being looped through, starting //with the incrementing of the hours wanted, then minutes, and //then moving onto the actual timing portion while (timerSet == false) { while (hourSet == true) { int timeinc = digitalRead(TIMEINCBUTTON); int nextbutton = digitalRead(NEXTBUTTON); //Taking in the reading of the button to tell when its being pushed //Changing the display to show the number of hours input if (timeinc == HIGH) { hourCount++; Serial.print("hours: "); Serial.println(hourCount); screen.setCursor(0, 1); screen.print("hours: "); screen.print(hourCount); delay(150); } //if moving on (next button being hit) then it goes onto telling //the user that they should increase MINUTES now if (nextbutton == HIGH) { delay(250); hourSet = false; Serial.println("next button hit, set minutes now"); screen.home(); screen.print("set minutes "); delay(1000); } } minSet = true; //As long as the minutes are still being set, the display will show the values while (minSet == true) { int timeinc = digitalRead(TIMEINCBUTTON); //Serial.print("minutes: "); if (timeinc == HIGH) { minCount ++; Serial.print("minutes: "); Serial.println(minCount); screen.setCursor(0, 1); screen.print("minutes: "); screen.print(minCount); delay(150); } //Here is the portion of code that was originally going to help me //cycle through functions and test buttons int nextbutton2 = digitalRead(NEXTBUTTON); if (nextbutton2 == HIGH) { //Serial.println("test"); minSet = false; timerSet = true; lastTime = millis(); Serial.println("Minutes set. Timer starts now"); screen.home(); screen.print("timer started "); delay(1000); } } } //After the timer is set, the counter starts while (timerSet) { int sleepyTime = hourCount * 10000 + minCount * 1000; //hours changed to 10 seconds and minutes changed to seconds for testing purposes screen.home(); screen.print("Sleepy time "); //This finds and displays the number of seconds passed if (snoozeCount == 1) { Serial.println((millis() - lastTime) / 1000); screen.setCursor(0, 1); screen.print("sec passed: "); screen.print((millis() - lastTime) / 1000); delay(1000); } //This makes it so that there is a display change when the alarm goes off if (((millis() - lastTime) >= sleepyTime) && snoozeCount == 1) { //Serial.println("alarm goes off"); screen.home(); alarm = true; screen.print("ALARM! AWAKE!"); //delay(2000); //While the alarm is supposed to be going off, this makes the active //buzzer activate while (alarm == true) { digitalWrite(BUZZERPIN, HIGH); delay(1000); digitalWrite(BUZZERPIN, LOW); delay(1000); int snoozle = digitalRead(SNOOZEBUTTON); if (snoozle == HIGH) { alarm = false; lastTime = millis(); } } //resets the time count lastTime = millis(); } //After the snooze button is hit, there is a set 10 second increment //before the alarm goes off again. This is for testing, otherwise it //would be minutes if (snoozeCount > 1 && alarm == false) { int snoozeTime = 10000; //snoozes for 10 min -- adjusted to 10 seconds for now Serial.println((millis() - lastTime) / 1000); screen.home(); screen.print("snoozin "); screen.setCursor(0, 1); screen.print("time snzd: "); screen.print((millis() - lastTime) / 1000); screen.print(" "); delay(1000); //This set of code checks whether the snooze time has been reached and //also activates the alarm print and sound if (millis() - lastTime >= snoozeTime) { Serial.println("alarm goes off pt 2"); screen.home(); screen.print("ALARM! AWAKE!"); alarm = true; while (alarm == true) { digitalWrite(BUZZERPIN, HIGH); delay(1000); int snoozle = digitalRead(SNOOZEBUTTON); if (snoozle == HIGH) { alarm = false; } digitalWrite(BUZZERPIN, LOW); delay(1000); snoozle = digitalRead(SNOOZEBUTTON); if (snoozle == HIGH) { alarm = false; } } lastTime = millis(); } } //This reads whether the snooze button is being pressed or not int snoozle = digitalRead(SNOOZEBUTTON); //here I create a change in the display such that it says //what is happening, a thought process of the actions it takes if (snoozle == HIGH) { alarm = false; Serial.println("alarm turned off"); Serial.println("snooze button hit"); //delay(1000); screen.home(); screen.home(); screen.print("sprayed "); screen.print(snoozeCount); screen.print(" times"); delay(1000); //This loop makes it so the spray bottle is squeezed //according to the number of times taht the snooze //has been pressed for (int i = 1; i <= snoozeCount; i++) { Serial.println("spray bottle"); servoMotor.write(180); delay(2000); servoMotor.write(0); delay(2000); } snoozeCount++; delay(500); } if (NEXTBUTTON == HIGH) { timerSet = false; hourSet = true; minSet = false; } } }
]]>
Overall photo of the chameleon necklace.
TCS34725 RGB sensor at the center of the necklace reads the color of surrounding objects.
Wire-wrapped acrylic beads are placed on top of the color-changing RGB LEDs.
The backside of the necklace pendant includes the Teensy3.2 microcontroller, a slide switch, and both RGB LEDs that illuminate the acrylic beads.
The necklace displaying the color of the red breadboard.
The necklace displaying the color of the green breadboard.
The necklace displaying the color of the blue breadboard.
The necklace changes color to match the breadboards held in front of the RGB sensor. The switch on the backside of the protoboard is flipped to keep a given color displayed on the necklace beads.
The first big decision of this project was to use the Teensy3.2 microcontroller over the Attiny85. Although the small size of the Attiny85 made it ideal for jewelry, it wasn’t compatible with the tcs34725 RGB color sensor library. By switching to Teensy3.2, my necklace was a bit bulkier, but this microcontroller was much easier to code with and compatible with all the libraries I needed for my project.
The second big decision I made was to change my project concept from chameleon earrings to a chameleon necklace. I realized that the dangling wires connecting the RGB sensor on a hidden wristband/necklace to a pair of earrings might be uncomfortable for the user. It felt more practical to contain all of the hardware on a single necklace.
Chameleon Earrings “works-like” prototype
I changed my project to a chameleon necklace based on this sketch after the prototype presentations.
Comparing the tcs34275 RGB sensor readings of various colors to their actual RGB values.
While soldering the electronic circuit for the necklace, the left side of the necklace was not lighting up. I had to re-solder the wires connecting the output pins to the LEDs on the left side after I realized they were loose.
For the final step of my project, I created a herringbone wire wrap for the acrylic beads and a wire chain. I learned both techniques from YouTube tutorials.
My original goal for this project was to create a pair of “chameleon” earrings with LEDs that would light up in the same color as an object placed in front of an RGB sensor. I created a “works-like” prototype of the earrings for the prototype presentation. A common concern in the feedback I got from the class was that the earrings would have a lot of dangling wires, which might make them hard to wear and very fragile. One classmate suggested that I “try wireless if want to use for earring.” While I originally wanted to have a wireless Bluetooth connection between the earrings and microcontroller/RGB sensor set-up, Zach warned me that the Bluetooth chips we had were probably too big to be used for earrings. I got another comment that “if it is hanging on your neck, maybe you could even turn that into a necklace.” I was initially hesitant to go down this route since I wanted to hide the electronics and feature the color-changing LEDs in the jewelry. However, after doing more sketches of a revised design, I realized that the protoboard and RGB sensor could also look nice as part of the necklace. Moving forward, I changed my project to be solely a necklace instead of earrings with the electronics on a hidden necklace or wristband.
While working on the chameleon necklace, I experienced the difficulties of creating smaller-scale electronic devices, especially wearables. I wanted to use the smallest possible microcontroller and fit all the electronics onto the smallest possible protoboard to reduce the bulk of the necklace pendant. I started with the Attiny85 and spent almost a week searching for ways to make the Tcs34725 RGB sensor library compatible with this microcontroller. After running into many dead ends, I switched to the Teensy3.2 microcontroller. In hindsight, I wish I didn’t get so hung up on using the Attiny85. If I had moved on to Teensy3.2 earlier, I would have had more time to assemble my necklace at the end, which was another challenging part of this project. While constructing the final necklace, I had to extensively plan how to fit all of the wires, resistors, sensors, and actuators on the small protoboard. Although it was frustrating to resolder the many loose/broken wire connections I found, this process helped me strengthen my soldering skills.
Overall, I feel like I achieved most of my goals for this project. My necklace can display most rainbow colors and has an eye-catching design. I still think my soldering could be improved as the final necklace still felt fragile, leading to a broken wire on the final critique day.
In the future, I would try to recreate this necklace using the more flexible stranded wires rather than the solid wires, which are prone to breaking. Also, I would like to have four LEDs on the necklace to draw more attention to the color-changing beads rather than the RGB sensor/microcontroller component.
/* 60-223 Chameleon Necklace Lynn Rushkin Description: This code takes the RGB reading from the TCS34275 RGB sensor and sends this RGB value to two RGB LEDs to output light of that color. When the slide switch is in the "on" position, the TCS34275 sensor will take new color readings. When the switch is "off", the sensor will stop taking color readings and the RGB LEDs will continue to display the last color reading the sensor took. Pin mapping: Arduino pin | type | description ------------|--------|------------- 10 output RGB LED pin for color output 17 input slide switch pin that reads on/off position of the switch 20 output RGB LED pin for color output The following sources were consulted to create this code: https://courses.ideate.cmu.edu/60-223/s2022/tutorials/button-and-switch https://learn.adafruit.com/adafruit-color-sensors/library-reference Example code "simple" from the Adafruit_NeoPixel library Example code "tcs34725" from the Adafruit_TCS34725 library */ //libraries used #include <Wire.h> #include "Adafruit_TCS34725.h" #include <Adafruit_NeoPixel.h> //pin names #define PIN1 20 #define PIN2 10 const int switchPin = 17; //other variables float red, green, blue; //defs for RBG LED #ifdef __AVR__ #include <avr/power.h> // Required for 16 MHz Adafruit Trinket #endif // I used a common Anode RGB LED #define commonAnode true //define number of RGB LEDs per strand #define NUMPIXELS 1 // LED strand length of 1 (both RGB LEDs I used were connected to their own output pins) //name the LED strips "pixels1" and "pixels2" , both containing only 1 RGB LED at PIN1 and PIN2, respectively. Adafruit_NeoPixel pixels1(NUMPIXELS, PIN1, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel pixels2(NUMPIXELS, PIN2, NEO_GRB + NEO_KHZ800); //define RGB LEDs brightness: #define BRIGHTNESS 50 // Time (in milliseconds) to pause between pixels #define DELAYVAL 250 // our RGB -> eye-recognized gamma color byte gammatable[256]; Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X); void setup() { pinMode(switchPin, INPUT); tcs.begin(); if (tcs.begin()) { Serial.println("Found sensor"); } else { Serial.println("No TCS34725 found ... check your connections"); while (1); // halt! } // Thanks PhilB for the following gamma table. // It helps convert RGB colors to what humans see for (int i = 0; i < 256; i++) { float x = i; x /= 255; x = pow(x, .5); x *= 255; if (commonAnode) { gammatable[i] = 255 - x; } else { gammatable[i] = x; } } pixels1.begin(); // INITIALIZE NeoPixel strip object pixels1.setBrightness(BRIGHTNESS); pixels2.begin(); // INITIALIZE NeoPixel strip object pixels2.setBrightness(BRIGHTNESS); } void loop() { int switchVal = digitalRead(switchPin); //The following if-statement ensures that new RGB readings are taken from //the sensor only when the switch is on. if (switchVal == 1) { tcs.setInterrupt(false); // turn on LED delay(60); // takes 50ms to read tcs.getRGB(&red, &green, &blue); tcs.setInterrupt(true); // turn off LED } else { tcs.setInterrupt(true); } //print the following lines of code to check the RGB sensor readings Serial.print("R:\t"); Serial.print(int(red)); Serial.print("\tG:\t"); Serial.print(int(green)); Serial.print("\tB:\t"); Serial.print(int(blue)); Serial.print("\n"); /*After collecting RGB readings from the tcs34275 sensor with all the rainbow colors, I found that the readings never dropped below 20 or got higher than 200. To acheive a closer color match in the LED output to the object color input, I mapped the sensor readings from the range of 20 - 200 to the range 0 - 255 for modified color values. */ int redMod = map(int(red), 20, 200, 0, 255); int greenMod = map(int(green), 20, 200, 0, 255); int blueMod = map(int(blue), 20, 200, 0, 255); // Send the updated pixel colors to the RGB LEDs. pixels1.setPixelColor( 0, pixels1.Color(int(redMod), int(greenMod), int(blueMod)) ); pixels2.setPixelColor( 0, pixels2.Color(int(redMod), int(greenMod), int(blueMod)) ); pixels1.show(); pixels2.show(); delay(DELAYVAL); // Pause before next pass through loop }
]]>
The choker compared to a quarter dollar coin
The front look of the choker
The piezo that detects the vibration on the neck and the microphone that detects and records sound
The choker doesn’t begin recording until the sound volume and the level of vibration both reach the thresholds
The audios will be saved to the SD card
Process
The first change I recorded is the choice of vibration detector, I was using a vibration sensor with a digital output at first, and it turned out that it cannot detect small vibration on the neck very well, then I changed it into a piezo.
The circled device is the vibration sensor I was originally using
The second change is the overall appearance of the choker, I was thinking about only leaving the Arduino Nano outside, but in the end I flipped it over to make the choker more flexible to wear.
The original design – outside
The origin design – inside
I flipped it over so that it’s really flexible and comfortable to wear
There are also changes in code that I forgot to record, I was using a naïve for loop calculating the average value to reduce noise but it didn’t work very well, later I use a Kalman filter instead.
Discussion
There are several comments regarding the appearance of the choker, some people are saying “the Arduino Nano as a ‘jewel’ is pretty neat looking, very high tech esthetic”, there are also people suggesting “hide the electronic board in the choker” or “hide all the electronics beneath some crystal/stone decor”. There are also comments regarding the user experience, people are saying “Softening the wires against your neck” and “Assembly and build is challenging to make robust and comfortable”. In the end, the choker turns out to be super cyberpunk due to me trying to make it comfortable to wear given limited hardware. Cyberpunk fans may like it while fans of traditional aesthetics may hate it. We don’t have wires in the lab that are soft enough, and the design of the SD card module make the choker super uncomfortable to wear if I attach the module to the choker. In fact, the original design is already a compromise, my idea is actually to make it into a modern silicon choker where the electronic components are all hidden in it, but due to limitations, this is how the final product looks like.
I’m personally not very satisfied about this project, because it doesn’t look very stable and I actually ran into an uploading issue when I finally decided to upload the code to Arduino Nano. There are some issues in the code as well, and I still cannot figure that out. A lot more extra work is needed in order to make it really satisfy me.
What I learned from this project is that it’s hard to deal with unfamiliar hardware and software libraries, my life would be much easier if I don’t try so many weird stuff. My original thought was just beyond the scope of this course, that’s not something I can finish using limited materials in such limited time. Apart from the hardware part, I was originally also thinking about writing code to make the choker actually distinguish between humming and other sound. I wanted to make something really useful that no other things can achieve the same result. My advice to my past self would be: don’t be too ambitious, why not just make a calorie tracker? You can definitely track it on your phone but it’s still a good little thing to have.
If I have chance to build another iteration of this project, I will debug the software first, and also acquire necessary materials to build a safe, stable, and good-looking choker. I will try to use Bluetooth instead of using SD card, and I will write code for distinguishing between music sound and noise.
Block Diagram
Schematic Diagram
Code
/* Music Composer Helper This code enables the choker to record for 3 minutes when the volume and vibration reach their thresholds, and save the audio to the SD card. */ /* Hardware Pinout Connection Arduino Nano SD Pin 5V ------------ VCC GND ----------- GND D10 ----------- CS D11 ----------- MOSI D12 ----------- MISO D13 ----------- SCK ________________________________________ Arduino Nano Microphone 5v ------------- VCC GND ------------ GND A0 ------------- Out ________________________________________ Arduino Nano Piezo GND ---------- negative A5 ----------- positive */ /* credit https://circuitdigest.com/microcontroller-projects/simple-arduino-voice- recorder-for-spy-bug-voice-recording */ #include <SimpleKalmanFilter.h> #include <TMRpcm.h>//the library for recording #include <SD.h> #include <SPI.h> #define SD_CSPin 10 #define vib_pin A5 #define mic_pin A0; const int sample_rate = 16000; int file_number = 0; int vib = 0; int vol = 0; unsigned int sample; float estimated_vol = 0; float estimated_vib = 0; char filePrefixname[50] = "rec"; char exten[10] = ".wav"; TMRpcm audio; /* SimpleKalmanFilter(e_mea, e_est, q); e_mea: Measurement Uncertainty e_est: Estimation Uncertainty q: Process Noise */ SimpleKalmanFilter volumeKalmanFilter(1, 1, 0.01); SimpleKalmanFilter vibrationKalmanFilter(1, 1, 0.01); // delay function for serial log. void wait_min(int mins) { int count = 0; int secs = mins * 60; while (1) { Serial.print('.'); delay(1000); count++; if (count == secs) { count = 0; break; } } Serial.println(); return ; } void setup() { Serial.begin(9600); //Sets up the pins pinMode(mic_pin, INPUT); pinMode(vib_pin, INPUT); //Checking SD card Serial.println("loading... SD card"); if (!SD.begin(SD_CSPin)) { Serial.println("An Error has occurred while mounting SD"); } while (!SD.begin(SD_CSPin)) { Serial.print("."); delay(500); } audio.CSPin = SD_CSPin; } void loop() { //print volume vol = analogRead(mic_pin); estimated_vol = volumeKalmanFilter.updateEstimate(vol); Serial.print("vol "); Serial.println(estimated_vol); //print vibration vib = analogRead(vib_pin); estimated_vib = vibrationKalmanFilter.updateEstimate(vib); Serial.print("vib "); Serial.println(estimated_vib); //print file name char fileSlNum[20] = ""; itoa(file_number, fileSlNum, 10); char file_name[50] = ""; strcat(file_name, filePrefixname); strcat(file_name, fileSlNum); strcat(file_name, exten); Serial.print("New File Name: "); Serial.println(file_name); //record if volume and vibration hit thresholds if (estimated_vol > 510 && estimated_vib > 1) { audio.startRecording(file_name, sample_rate, mic_pin); vol=0; vib=0; estimated_vol = 0; estimated_vib = 0; Serial.println("startRecording "); // record audio for 3mins wait_min(3); audio.stopRecording(file_name); Serial.println("stopRecording"); file_number++; } //clear values to avoid bugs vol=0; vib=0; estimated_vol = 0; estimated_vib = 0; }
]]>
A servo-activated alarm clock that instantly wakes you up by spraying water.
Short clip of alarm clock
Close up shot of the alarm clock
Top-view of input alarm
Front view of input alarm
Close up of trigger mechanism
The insides of the box which contains all the other parts.
The RTC module was instrumental in keeping track of time
Initial design to place Servo in front of trigger
Final position for Servo behind bottle that pulls the trigger
Initially, I envisioned the servo to trigger the water spray by pushing against the trigger from the front. This was conceptualized based on a previous project along the same lines. This was not feasible as I could not create a practical build where the servo provides sufficient force to trigger the water spray. However, through the guidance of the professor, he suggested to change the servo’s position to back of the water spray for a pulling mechanism which would be more efficient in triggering the water spray. This change in design ensured that proper functioning of my project!
Initial design to build all hardware on water spray
Final design with separate box for ardunio and other wiring.
My original design aspired for the alarm clock to be as compact as possible. As seen in my sketch, I wanted all the hardwares to be mounted on the water spray so it could occupy the least space on my side table. My initial trials were futile as I tried to tape and super glue the arduino and LCD display on the water spray with a box that covers it. This was potentially practical when the servo motor was at the front of the water spray. However, the servo motor required a base behind the water spray which meant none of these designs could be accomplished. So, I used laser cutting to create a separate box that included all my other hardware. Nevertheless, this change appeared to be most practical and visually appealing.
“Such a useful device that definitely wakes me up.”
This was definitely my intention, as a person who frequently misses their morning alarms, I wanted to create a device that wakes me up in the first go. Though the mist is quite refreshing in the first spray, it gets annoying when it triggers multiple times when the alarm rings!
“Do you like to sleep with wet pillows?”
This was something I did not consider! I did not foresee that it would make my bed wet after it sprays a number of times. Thankfully, the water spray only sprays mist as compared to water droplets which mitigates this issue. However, I would have to find waterproof bedding now onwards.
I was extremely delighted when I first saw the water spray being triggered by the servo based on the alarm time. It met the basic functionality that I had originally envisioned. I couldn’t wait to try it out in my room when it worked in the physical computing lab. The first few tests seemed to work perfectly but it filled me with dread upon realising that it would ruin my mornings if it disrupted my dreams or sleep cycles.
This project provided insights into the process of designing and building prototypes which I have never done before in my life. The structured delivery timeline ensured I was on time for all my deliverables while providing incentive to work ahead of the deadline. There were times where I was at the end of my wits when the servo did not trigger as expected. During the process, I found myself stuck in a particular way of thinking which limits my creativity and in this case, without the help of the professor, I would not have been able to overcome it. It emphasized the importance of having some degree of flexibility from the design of the prototype in the actual implementation as we cannot foresee everything from the get-go.
After finishing the project, I planned to 3D print a build that accomodates everything on the water spray but eventually dropped the idea to focus on other upcoming deliverables. I would try to create a build with a smaller base for the servo motor using other alternates besides styrofoam. Additionally, would try to find a way to screw the servo into the water spray or the base to ensure that it does not come off loose as the current project is hot glued together and might come off after frequent number of triggers. I would incorporate other design changes such as adding a snooze function which is not a simple switch at the top, add a water level sensor to remind the user when the bottle is empty.
Block diagram
Schematic
/* Project Title: Water-spray Alarm Clock Name: Siddharth Parthiban Description: User sets the time for the alarm by rotating the two rotary encoders whereby controlling the hours and the minutes which is displayed by the LCD Screen. The RTC module provides the input for the current time and when both the alarm and the current time is the same, the code directs the servo to rotates 40 degrees back and forth to trigger the water spray. Additionally, as a snooze button, the switch controls whether the water spray is triggered multiple times during the alarm duration. Pin Mapping: Arduino pin | role | description ------------|---------|------------- 2 input rotary encoder(hour) pin A 3 input rotary encoder(hour) pin B 5 input rotary encoder(min) pin A 6 input rotary encoder(min) pin B 8 output Servo motor 10 input switch SCL input RTC Module/LCD SDA input RTC Module/LCD Rotary Encoder Code adapted from https://dronebotworkshop.com/rotary-encoders-arduino/#Arduino_Motor_Encoder_Sketch DroneBot Workshop 2019 */ #include <Encoder.h> #include <Servo.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <DS3232RTC.h> //Initialising variables for user input long rotatMin = 0; long prevRotatMin = -999; int alarmMinute; int alarmHour; int curMinute; int curHour; long rotatHour = 0; long prevRotatHour= -999; //Initialising variable for output int servoDegree = 20; float oldTimeDisplay = 0; //Initialising pin ports for Arduino int SWITCHPIN = 10; const int SERVOMOTORPIN = 8; //Creating Servo, Rotary Encoder and RTC module objects Servo servoMotor; Encoder hourEncod(2,3); Encoder minEncod(5,6); DS3232RTC myRTC; //Declaring LCD screen object LiquidCrystal_I2C screen(0x27, 16, 2); void setup() { Serial.begin(9600); //setting data rate pinMode(SWITCHPIN, INPUT); //declaring switch for arduino //Setting up servo motor servoMotor.attach(SERVOMOTORPIN); servoMotor.write(servoDegree); //Setting up LCD Screen screen.init(); screen.backlight(); screen.home(); } void loop() { time_t t = myRTC.get(); //Get current time //Read user input from rotary encoder rotatMin = minEncod.read(); rotatHour = hourEncod.read(); //Calculate the minutes based on rotations if (rotatMin != prevRotatMin){ prevRotatMin = rotatMin; //Rotations divided by 4 to set an arbitrary unit for 1 turn of the knob //Modulo 60 to get convert turns to respective minutes //if-else statements to consider anti-clockwise turns if (rotatMin < 0){ alarmMinute = (-(rotatMin/4)) % 60; } else { alarmMinute = (rotatMin/4) % 60; } //Display set minutes to LCD Screen screen.setCursor(12,1); screen.print((String)alarmMinute); } //Calculate hour based on rotations if (rotatHour != prevRotatHour){ prevRotatHour = rotatHour; //Rotations divided by 4 to set an arbitrary unit for 1 turn of the knob //Modulo 24 to get convert turns to respective hour //if-else statements to consider anti-clockwise turns if (rotatHour < 0){ alarmHour = (-(rotatHour)/4) % 24; } else { alarmHour = (rotatHour/4) % 24; } //Display hour on LCD Screen screen.setCursor(9,1); screen.print((String)alarmHour + ":"); } //Move servo motor if not on snooze to trigger water spray if (digitalRead(SWITCHPIN) == 0){ //Check if current time is equal to alarm time if (minute(t) == alarmMinute && hour(t) == alarmHour){ servoMotor.write(servoDegree + 90); delay(500); servoMotor.write(servoDegree - 50); delay(1000); servoMotor.write(servoDegree + 90); delay(500); servoMotor.write(servoDegree - 50); } } //Reset servo motor to neutral position servoDegree = 20; servoMotor.write(servoDegree); //Display values to LCD Screen if (millis() - oldTimeDisplay >= 250) { oldTimeDisplay = millis(); screen.clear(); screen.setCursor(2, 0); screen.print("Time"); screen.setCursor(2, 1); screen.print((String)hour(t)+ ":" +(String) minute(t)); screen.setCursor(8, 0); screen.print("Alarm"); screen.setCursor(9, 1); screen.print((String)alarmHour + ":" + (String) alarmMinute); } }
]]>