Project 2 – Intro to Physical Computing: Student Work Spring 2022 https://courses.ideate.cmu.edu/60-223/s2022/work Intro to Physical Computing: Student Work Wed, 16 Mar 2022 14:10:13 +0000 en-US hourly 1 https://wordpress.org/?v=5.8.9 The Nay Phone Inator https://courses.ideate.cmu.edu/60-223/s2022/work/the-nay-phone-inator/ Wed, 16 Mar 2022 14:09:33 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15663 A device to help lock away your phone for a specified amount of time so you can focus on tasks at hand.

An overall photo for portion and scale.

The “self destruct” button

The LED strip on a wall of the box.

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

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

Process Images and Review

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

self destruct button

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

first process image with components i intended to include

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

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

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

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

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

Discussion

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

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

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

Technical Information

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

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

*/

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

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

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

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

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

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

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

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

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

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

}

void loop() {

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

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

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


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

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

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

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

      lcdTimer = millis();
    }
  }

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

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

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

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


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

  if (countdownMode) {

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

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

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

      if (totalTimerSeconds >= 0) {

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

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

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

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

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

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

        if (selfDestructButtonState == HIGH) {

          //setting a destruct countdown
          destructTimer++;

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

          if (destructTimer < 10) {

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

          else {

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

        if (destructPassed) {

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

          //servo motor: unlock the door

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

        }

      }

      else {
        //TIMER HAS BEEN ENDED

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

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

        //servo motor: unlock the door

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

        //LED STRIP: updating the LED strip with congratulatory colors
        // Update the colors.
        byte time = millis() >> 2;
        for (uint16_t i = 0; i < LED_COUNT; i++)
        {
          byte x = time - 8 * i;
          colors[i] = rgb_color(x, 255 - x, x);
        }
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
    }
  }
}

// THE FUNCTIONS I'M USING

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

 

]]>
Exercise Set Tracker https://courses.ideate.cmu.edu/60-223/s2022/work/exercise-set-tracker/ Wed, 16 Mar 2022 13:54:22 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15683

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.

Process

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.

Discussion

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.

Technical Information

 

/*  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 https://courses.ideate.cmu.edu/60-223/s2022/work/math-buddy/ Wed, 16 Mar 2022 12:15:50 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15641 Math Buddy - Cover Image

Math Buddy

Overview

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

Math Buddy – Rear View

Math Buddy - Smiling Face with "Correct" Hand

Smiling Face with “Correct” Hand

Math Buddy - Sad Face with "Wrong" Hand

Sad Face with “Wrong” Hand

 

Process

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.

Math Buddy - Testing input and LCD

Testing Keypad input and LCD

Math Buddy - Putting All Electronics Together

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.

Math Buddy - Container Design

Container Design and Laser Cutting file Prep.

Math Buddy - Assembling the Machine

Assembling the Machine

Math Buddy - Organizing Wires

Organizing Wires

 

Discussions

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.

Block Diagram

Math Buddy - System Diagram

System Diagram

Electronic Schematic

Electronic Schematic

Electronic Schematic

Code

/*
  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!!");
}

 

]]>
The Good Habits Checklist https://courses.ideate.cmu.edu/60-223/s2022/work/the-good-habits-checklist/ Wed, 16 Mar 2022 08:27:19 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15624 For Project 2, I created a checklist that rewards me for establishing good habits.

Photos and Videos

Overall Photo

The candy funnel seen without candy.

The candy funnel seen with candy.

Internal Wiring

Process Images

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.

Discussion

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.

Technical Information

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 https://courses.ideate.cmu.edu/60-223/s2022/work/project-2-smart-hat-with-rotatable-brim/ Wed, 16 Mar 2022 06:30:30 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15277 60223 Project 2 – David Wu

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();
  }
}

 

]]>
“Don’t Forget Your Things!” https://courses.ideate.cmu.edu/60-223/s2022/work/dont-forget-your-things/ Wed, 16 Mar 2022 06:19:54 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15474 This device reminds me to bring my things with me in the morning. If I have everything listed with me, a latch opens and my ID falls out into a tray, allowing me to leave my house.

Overall image

 

panel mount detail

servo latch detail

LED detail

Flipping the switches

Lid opens and you get your ID

Putting ID back into device and resetting it

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.

Block diagram

schematic diagram

/* "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;
  }
}
]]>
The Anti-Snooze Machine https://courses.ideate.cmu.edu/60-223/s2022/work/the-anti-snooze-machine/ Wed, 16 Mar 2022 05:47:31 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15529 My project 2 was an alarm clock specifically meant for taking naps and incentivizing you to not hit the snooze button.

  • Here is the machine in action, spraying water out as the snooze button is hit.

 

 

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.

 

 

Process Images and Review

 

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.

 

Discussion

    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.

Technical Information

Block Diagram

 

Schematic Diagram

 

//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;
    }

  }
}

 

]]>
Chameleon Necklace https://courses.ideate.cmu.edu/60-223/s2022/work/chameleon-necklace/ Wed, 16 Mar 2022 05:45:15 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15494 The chameleon necklace changes color to match its surroundings, allowing users to match their jewelry to their outfits.

Overall photo of the chameleon necklace.

Detail Photos:

 

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.

Necklace in Use:

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.

Video:

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.

Process:

Decision 1:

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.

Decision 2:

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.

 

Process Images:

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.

Discussion:

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. 

Block Diagram:

 

Electrical Schematic:

Code:

/* 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
}

 

]]>
Music Composer Helper https://courses.ideate.cmu.edu/60-223/s2022/work/music-composer-helper/ Wed, 16 Mar 2022 05:38:48 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15316 This is a choker that can start recording autonomously when it detects the user humming unconsciously in the bathroom.

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;
}

 

]]>
Project 2: Water-spray alarm clock https://courses.ideate.cmu.edu/60-223/s2022/work/project-2-water-spray-alarm-clock/ Wed, 16 Mar 2022 05:17:20 +0000 https://courses.ideate.cmu.edu/60-223/s2022/work/?p=15281 Water-spray Alarm Clock

A servo-activated alarm clock that instantly wakes you up by spraying water.

 

Images

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

Decision Points

  1. Switching the servo’s position

     

    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!

  2. Creating a separate build for the arduino, LCD and rotary encoders

    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.

Critiques

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.

Discussion

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.

Schematic and block diagrams

Block diagram

Schematic

Code

/*
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);

  }
}
  

 

 

 

]]>