Project 2 – Intro to Physical Computing: Student Work spring 2019 https://courses.ideate.cmu.edu/60-223/s2019/work Intro to Physical Computing: Student Work Wed, 15 Sep 2021 20:28:12 +0000 en-US hourly 1 https://wordpress.org/?v=5.0.21 The Medicine Remembrall https://courses.ideate.cmu.edu/60-223/s2019/work/the-medicine-remembrall/ Mon, 25 Mar 2019 13:16:12 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=7018 A spherical glass box that glows in multicolored gradients each morning to remind the user to take their prescription medicine, and turns off and displays the time opened after the user opens the box.

Overall view of the Medicine Remembrall while it is glowing

Close up on OLED display and electronics box after the box has been opened

Close up on opened box, IR monitor, and space for prescription bottle

A short clip of the device in use, including being opened.

A video of the full color spectrum the Remembrall cycles through.

Progress

These were my original sketches of the idea. I experimented with the idea of using a hall sensor or an accelerometer to detect the opening of the box.

This was the cardboard version of the original box design which I used to test the hall sensor with the opening and closing of a box. Unfortunately, the hall sensor kept yielding unreliable results due to the magnetism of the breadboards, Arduino, and my laptop, and I quickly realized that an IR sensor would be easier to use, because unlike if I used an accelerometer, I would not have to attach any electrical components to the lid of the box.

Wiring the LEDs and OLED display when I had considered using the Arduino’s own timing mechanism to set the intervals at which the LEDs reset and have the display show the time since opening

Using the Arduino’s built in timing mechanism didn’t make sense as it meant that the time would be reset every time the device was unplugged, and doing the conversions between milliseconds elapsed since the box was opened to a normal time display of hours, minutes, and seconds, seemed like unnecessary work, so I added in a seperate Real Time Clock component.

A video of the LED strips initializing as the time hits 9:00:00

My electronic components could not fit in the tight space of the initial box design without becoming significantly messier, so I decided to place them in a separate box, allowing for a more creative design for the actual lighted box.

Because my design options for the box were no longer limited by fitting the electronics inside of it, I decided to switch from using frosted acrylic to diffuse the LEDs to wrapping them around the inside of a glass bowl to be reflected and diffused by the central cylinder, which hides the prescription bottle. This was able to create an effect of glowing from the middle similar to a Remembrall in Harry Potter, a sphere which glows if its user has forgotten something, which is where the final project got its name.

Discussion

It’s tough to demo time-based things. Might be advantageous to have a debug mode, just for demo purposes.”

During the critiques, I simply changed the time when the LEDs initialize in the code so that the class could watch the LEDs turn on due to the time. If I had planned this better, I would’ve had this time as a variable at the top of the code so I could edit it easier, or I could simply have set the code to begin the first time it is uploaded as if the box hadn’t been opened yet, by setting the initial value of the variable timeYet to 1.

Soldering your items might be good. Otherwise, the box is well thought of to hide all the wiring.

After receiving my critiques, I cleaned up my wiring and covered the elements outside the two boxes in electrical tape, but if I had had additional time, I think I definitely should’ve soldered the items that I connected through female-to-male jumpers.

While I find the remembrall-inspired glowing effect from the box incredibly beautiful, my primary critique for myself is not leaving enough time to work on the physical design. If I’d had more time, I would’ve soldered all of the wiring and used a more durable material than construction paper as the diffuser inside the remembrall. I’m very happy with the concept behind it, and in the past week of use, it has improved my life by reminding me to take my meds daily, and only once daily. I find myself checking the time on the box to confirm when I took my meds at various times throughout the day because I don’t remember taking them and would otherwise have the urge to take them a second time. Since this is something that I use every day, I do intend to devote more time to improving it in the future, by creating a more durable box for the middle of the remembrall and to hold the electronics, and soldering the wires. Ideally, I would drill a hole in the bottom of the bowl so the wiring didn’t have to stick out of the top, but the current bowl is glass, which would make this difficult. I might eventually go back to the initial idea of laser-cutting an acrylic box that can fit both the electronics and functional part of the device, but I am very attached to the remembrall theme that is created through the round shape and diffusion from the center. The most important lesson to me from working on this project was not the result of a specific component or design choice; through a combination of different ultimately scrapped approaches, I strengthened my ability to realize when an idea like the using the hall sensor or converting the arduino’s found time from milliseconds to hours, minutes, and seconds was not worth the extra level of time that it would require to make right, and to move on to a new approach. My personal impulse is to always finish everything I started exactly how I started it, and if something isn’t working but is theoretically capable of working after I put a lot of time into repairing it, I tend to hyper focus on making that specific idea work, instead of moving on to something that will work more easily, which turns into a massive waste of time that could be better spent on a different part of the project. I also learned a lot about the Arduino’s capabilities to tell time and the existence of time variables and how to operate on them through splitting strings so that I can compare the value of the current time to a set alarm time to determine if it is time yet to initialize the LEDs.

Schematic

Code

/*
 * The Medicine Remembrall
 * Adrien Krupenkin, 2019
 * 
 * Sets an LED strip to start cycling through a rainbow gradient at a set time each morning, 
 * until an infrared detector senses that the box has opened, turning off the LEDs and displaying 
 * when the box was opened.
 */
#include <DS3231.h>
#include <Wire.h>

#include <Adafruit_SSD1306.h>
#include <splash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <gfxfont.h>
#include <DS3231.h>
#ifdef __AVR__
#include <Adafruit_NeoPixel.h>

  #include <avr/power.h>
#endif

#include "Arduino.h"
#include "SPI.h"


#define LEDPIN 2
#define IRPIN A0
#define OLED_RESET 4
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

Adafruit_SSD1306 disp(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
DS3231  rtc(SDA, SCL);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(30, LEDPIN, NEO_GRB + NEO_KHZ800);  //setup of LED strip

int x = 123;
int val;    // variable to read the value from the analog pin
int nLEDs = 30;   //value for number of LEDs used
int timeYet = 0;
Time t;
Time opened;
int displayTime;
int irVal = 100;
int Hor;
int Min;
int Sec;
int hourOpened = 0;
int minOpened = 0;
int secOpened = 0;
String openedTime;

void setup() {
  rtc.begin();
  
  // The following lines were uncommented in one upload to set date and time and recommented
 // rtc.setDOW(SUNDAY);     
 // rtc.setTime(11, 36, 00);     // Set the time
 // rtc.setDate(3, 3, 2019);   // Set the date
  Wire.begin();
  disp.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  pinMode(LEDPIN, OUTPUT);  
  pinMode(IRPIN, INPUT);
  Serial.begin(9600);
    disp.display();

  // Clear the buffer
  disp.clearDisplay();
   disp.display();
    strip.begin(); 
    strip.show();

}

void loop() {
  //Check time
   t = rtc.getTime();
   Hor = t.hour;
   Min = t.min;
   Sec = t.sec;
   if( Hor == 8 &&  (Min == 00 || Min == 01)){
        timeYet = 1; 
   }
   Serial.println(timeYet);
  uint16_t i, j;
  if (timeYet == 0) //if it's not time, turn off LEDs and display the time when the box was last opened
  {
    strip.setBrightness(0);
    strip.show();
    disp.clearDisplay();
    disp.setTextSize(1); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 0);
    disp.println("Time Opened:");
    disp.setTextSize(2); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 10);
    disp.println(openedTime);
    disp.display();
  }
  if (timeYet == 1) //if it's past the set morning light time, turn on LEDs and display time
  {
    strip.setBrightness(50);
    strip.show();   // write all the pixels out
  //loop to write LEDs based on color from wheel 
  for(j=0; j<256; j++) {  
    for(i=0; i<strip.numPixels(); i++) {
      if(timeYet == 1)
      {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    strip.show();
      }
    }
    //Display Time (do in this loop so it doesn't get delayed)
    disp.clearDisplay();
    disp.setTextSize(1); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 0);
    disp.println("Current Time:");
    disp.setTextSize(2); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 10);
    disp.println(rtc.getTimeStr());
    disp.display(); 
    irVal = analogRead(IRPIN); //check if the box has been opened
    Serial.println(irVal);
     if (irVal<100){
    openedTime = rtc.getTimeStr();
    timeYet = 0; //if the box has been opened, set the time setting to be not time yet for LEDs
  }
    }
    }
    irVal = analogRead(IRPIN);
    Serial.println(irVal);
     if (irVal<100){
    openedTime = rtc.getTimeStr();
    timeYet = 0;
    strip.setBrightness(0);
    strip.show();
    disp.clearDisplay();
    disp.setTextSize(1); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 0);
    disp.println("Time Opened:");
    disp.setTextSize(2); // Draw 2X-scale text
    disp.setTextColor(WHITE);
    disp.setCursor(10, 10);
    disp.println(openedTime);
    disp.display();
  }
}

//code to cycle through different LED colors
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

 

]]>
Backpack Feedback T-Shirt https://courses.ideate.cmu.edu/60-223/s2019/work/backpack-feedback-t-shirt/ Sun, 24 Mar 2019 20:48:58 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=7009 Overview

My project explores developing a garment that helps mountaineers understand how climbing load is distributed across their back as they train.

Top view of the garment

Detailed view of the protoboard and wires

Side view to show levels of sensor protrusion

Since the device prints serial output, a GIF / video would not help describe the garment and thus was not shown.

Process Images and Review

I’m currently training to climb a mountain in July, which involves doing Stairmaster for extended periods of time with a loaded backpack. All mountaineers use internal frame packs, which have flat, metal bars located against your back inside the pack that provide rigidity and support. These bars, combined with a hip belt that locks tightly around your midsection, distributes the bulk of the load to your legs rather than your back. Not only is this more comfortable for the climber, but it prevents back injuries and reduces stress.

I found that bad backpack adjustments and the way I walked led to injuries on my shoulder and lower back, which meant that during my training sessions, the pack wasn’t placed properly on my back and tended to move around. I researched exactly how the pack is supposed to be placed on a person and identified areas where the frame must touch the human’s back at all times.

Drawing of an internal frame pack

The pink regions are the parts of the backpack that must always be connected to the climbers back. I knew my injuries were coming from not connecting with these areas due to poor pack adjustment and bad walking posture, but I needed to figure how to measure which areas of my backpack were in the right place and which ones were not.

I decided to use force sensitive resistors (FSR) to measure if a certain area of the climbers back was being touched. I initially thought about attaching these sensors to the pack itself, but a garment made more sense, since it could be used with different packs for different situations.

A single FSR attached to a protoboard

I used the first sensor I connected to test out how the values would change based on a connection between the pack and the climber’s back.  I tried attaching thinly cut dowel slices to the back end of the FSR to create a flatter surface to more accurately measure force, but these ended up obfuscating the measurements due to their changing position when every step was taken. So I decided to tape the FSR’s directly on to the garment to create the best surface for measurement.

Fully connected sensor and Arduino system

After adding sensors (4 for the lower back and 2 for the mid-back) and soldering them to the protoboard, the hardware aspect of the project was complete. I had some trouble soldering at the beginning, but my skills were significantly better by the end.

Discussion

I received plenty of good feedback from my table group. One student said “I like that speaker that would tell you if you’re off balance or tilted” which was in reference to an original plan I had of integrating a speaker to let the climber know which way they were tilting based on the tone emitted. I agree that there needs to be some sort of sensory feedback, butt disagree with the method. Haptic feedback would be more useful since these training sessions are usually done at the gym and hearing a loud noise while doing strenuous exercise would not be pleasant.  Another student said “Creative idea! Nice choice doing a wearable. I would be excited to see the next iteration of this.” I think if I were to develop this as a product, I would think of a better way to integrate the sensors into the fabric, or to connect them wirelessly to a wearable like an Apple Watch.

I spent a lot of time figuring out sensor placement and soldering the sensors onto the protoboard, so I wasn’t able to build out some of the GUI functionality I wanted to. I tried out the speaker idea very initially and didn’t like the constant noise, but I wish I had created haptic feedback for the user. I was happy about the new skills I learned and how useful the project is to me, but i wish I had done more to package it and build out auxiliary features.

I found that I lack some creativity when it comes to creating a polished product. Coding isn’t too difficult and the hardware works in the end but having a fully-fleshed out project that is nice to look at has been a struggle. Seeing the projects of the design students has given me a good direction to start with for the final project, and I hope it turns out both functional and beautiful.

I would take a table members advice and integrate the sensors into the fabric of the shirt. I would also create a sensor array all along the back rather than 6 individual sensors and then handle the data from all of them at once. I’d also make all the connections to the sensor over Bluetooth so it could be integrated with an Apple Watch or Fitbit.

Technical Information

Code

// Project 2: Backpack Feedback T-Shirt
// This code reads data from 6 FSR's and prints out computed statistics
// timing code from http://forum.arduino.cc/index.php?topic=503368.0

const int TOP_LEFT = A0; // FSR is connected to analog 0
const int TOP_RIGHT = A1; // FSR is connected to analog 1
const int MIDDLE_LEFT = A2; // FSR is connected to analog 2
const int MIDDLE_RIGHT = A3; // FSR is connected to analog 3
const int BOTTOM_LEFT = A4; // FSR is connected to analog 4
const int BOTTOM_RIGHT = A5; // FSR is connected to analog 5
int TL_Reading;
int TR_Reading;
int ML_Reading;
int MR_Reading;
int BL_Reading;
int BR_Reading;

// timing code from http://forum.arduino.cc/index.php?topic=503368.0
unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 5000;  //the value is a number of milliseconds
int topCount;
int baseCount;
int totalPeriods;

void setup(void) {
  Serial.begin(9600);
  startMillis = millis();  //initial start time
  topCount = 0;
  baseCount = 0;
}
 
void loop(void) {
  TL_Reading= analogRead(TOP_LEFT);
  TR_Reading = analogRead(TOP_RIGHT);
  ML_Reading = analogRead(MIDDLE_LEFT);
  MR_Reading = analogRead(MIDDLE_RIGHT);
  BL_Reading = analogRead(BOTTOM_LEFT);
  BR_Reading = analogRead(BOTTOM_RIGHT);

  Serial.print("TL = ");
  Serial.print(TL_Reading);
  Serial.print(" - ");
  Serial.print("TR = ");
  Serial.print(TR_Reading);
  Serial.print(" - ");
  Serial.print("ML = ");
  Serial.print(ML_Reading);
  Serial.print(" - ");
  Serial.print("MR = ");
  Serial.print(MR_Reading);
  Serial.print(" - ");
  Serial.print("BL = ");
  Serial.print(BL_Reading);
  Serial.print(" - ");
  Serial.print("BR = ");
  Serial.print(BR_Reading);
  Serial.println(" - ");

  if (abs(TL_Reading - TR_Reading) > 20)
  {
    topCount += 1;
  }
  if (abs((ML_Reading + BL_Reading) - (MR_Reading + BR_Reading)) > 400 )
  //|| ((ML_Reading <= 10 || BL_Reading <= 10) && (MR_Reading <= 10 || BR_Reading <= 10))
  {
    if((ML_Reading + BL_Reading) > (MR_Reading + BR_Reading))
    {
      Serial.println("Tilting left...");
    }
    else {
      Serial.println("Tilting right...");
    }
    baseCount += 1;
  }
  currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {
    totalPeriods += 1;
    startMillis = currentMillis;  //IMPORTANT to save the start time
    Serial.print("Pack wrong on top: ");
    Serial.print(100 * (topCount/(totalPeriods * 100.0)));
    Serial.print("%, Pack wrong on base: ");
    Serial.print(100 * (baseCount/(totalPeriods * 100.0)));
    Serial.println("%");
  }
  delay(period / 100);
}

 

]]>
Humidifier base https://courses.ideate.cmu.edu/60-223/s2019/work/humidifier-base/ Sat, 23 Mar 2019 03:13:21 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6940 A companion device designed to complement and augment the functionality of a humidifier.
Ian Shei

Winters in Pittsburgh are long and cold, and with the brisk weather comes excessive heating of buildings. Heating through steam radiators invariably drives indoor relative humidity uncomfortably low, causing dry skin, nosebleeds, respiratory problems, and aggravated allergies and skin conditions. It can also dry nasal passages, leading to greater susceptibility in contracting cold viruses. Experts recommend keeping indoor relative humidity between 40 and 60% for comfort, reduction of airborne bacterial and viral transmission, and minimization of mite and fungal populations.

However, most humidifiers, including mine, don’t sense relative humidity and thus rely on manual activation in order to operate. This introduces much guesswork to the process – unless one has a humidity sensor handy, they most likely turn on the humidifier when it “feels” dry (humidity readings in weather forecasts don’t accurately reflect indoor conditions).

This often results in forgetting to turn on the humidifier if one is preoccupied or busy. Personally, the main reason I have a humidifier is because of sensitive skin, but I find that I could be using the humidifier when the cause for irritated skin at the moment is unrelated (stress, sleep deprivation, air pollution, etc.).

My humidifier. It sits on my desk in my room and is essential to surviving a Pittsburgh winter.

My objective with this project was to create a base for my humidifier that would noticeably signal when the relative humidity in a room had fallen below a set threshold, thus signaling that the humidifier should be turned on. Since monitoring of humidity is constant and automatic, the base could attract a person’s attention using lighting so that they don’t neglect increasing the humidity. By activating based on an environmental measurement, any guesswork in the indoor humidity is eliminated – the humidifier would only be turned on when necessary as dictated by the sensor’s reading.

Some notes and a quick phone sketch early in the process.

I began by wiring and testing this project’s essential component: an inexpensive DHT22 temperature and humidity sensor. To verify that the part functioned as described, I used an Arduino Uno to act as an intermediate between the DHT22 and the serial monitor on my computer.

The DHT22 sensor (the white block in the lower left corner of the breadboard) connected to an Uno.

With the humidity sensor verified to be working, I connected a rotary encoder, ambient light sensor, and a ring of 24 RGBW LEDs called a NeoPixel Ring. The NeoPixel’s purpose is to display the relative humidity through pulsing (the faster the pulsing, the drier the air) in order to alert the owner to turn on a humidifier. It also works with the rotary encoder to adjust the humidity level below which the LEDs will turn on, serving as a display to set the threshold. Finally, the ambient light sensor controls the brightness of the LEDs so as to not be blinding in a dark room.

The basic components (DHT and ambient light sensors, NeoPixel Ring, rotary encoder) connected to an Arduino. The rotary encoder pictured was used for testing and verification and was replaced with a smaller one later.

Because everything had to fit inside an enclosure approximately 5″ x 5″ x 1.25″, minimizing the footprint of every component and connection was key. I tried to use an ATTiny85 to handle and control all the components but was unable to get it to drive the NeoPixel in a reasonable amount of time, so I settled on using an Arduino Nano instead. While the rotary encoder pictured above had great resolution and was very robust (it’s commonly used in robotics applications), its z-height was too big to fit in the casing so I chose a smaller component instead.

Working out the programming with all components proved to be a challenge, even without the fuss of the ATTiny85. Appropriately sequencing and implementing logic was essential in managing all the possible modes of the system, while controlling the NeoPixel was essential in making visual feedback polished and natural. The pulsing of the LEDs is accomplished with a sine function, a smoothing algorithm ensures that sudden changes in ambient light don’t noticeably affect LED output, and millis() is used in favor of delays to make the system as responsive as possible. My thanks to Eric Mendez and Robert Zacharias, who were extremely helpful in troubleshooting and explaining the logic of implementing multiple aspects.

Once I had ensured that the electronics were working as designed, I optimized the LED brightness output vis-à-vis the ambient light sensor and added a simple piezoelectric buzzer to provide auditory feedback when setting the threshold humidity, corresponding with the NeoPixel. It’s better visualized in the video below:

A quick rundown of the status LEDs:

  • Blue pulsing ring – Relative humidity is below threshold humidity. The drier the air, the faster the pulsing.
  • Green ring – Relative humidity is at or above threshold humidity. Appears for a few seconds, then fades.
  • Individual LEDs turning on/off sequentially – Currently in edit mode; turn dial to adjust threshold humidity.

At the same time, I worked on designing an enclosure that would house all the components and also support the humidifier. Sketches of different profiles and arrangements of components gradually yielded a CAD model, seen below.

Space is a premium in the first “final” enclosure design.

I counted on a turntable from McMaster-Carr to be transparent like on the website (it was white). Since this arrangement had the NeoPixel directly under the turntable, I had to redesign almost all the internal components to use a different bearing. A positive side effect was that being forced to use a different bearing meant that I could use a smaller component, freeing up valuable space inside the enclosure.

With the enclosure design pretty much set, I 3D-printed and laser cut components. I originally wanted to use an SLA printer, but given that this was the first print (Form 2 prints are expensive) and the specific resin I wanted wasn’t available, I used a traditional FDM printer instead. With a little modification, the housing can also be made of wood and turned on a lathe.

During printing.

Post-print. Breaking off all the supports took some time.

Other parts of the enclosure, including the top dial, were made from laser cut acrylic derived from the same CAD file. Since the enclosure has a circular profile with a rotary encoder at the center, carefully aligning differently-sized components to be concentric was essential.

Bringing together the sub-assemblies of the enclosure.

Testing fit of the the NeoPixel-wiring-bearing subassembly within the enclosure.

With the enclosure parts made, it was time to transfer the electronics inside. I soldered them to a flexible PCB, thinking that the flexibility and easy resizability of the board would help fit all the parts inside the housing. However, testing upon solder completion revealed that the connections were already compromised. The PCB is able to flex, but I hadn’t considered that flexing solder points would eventually cause them to crack and separate from the board. In the end, the electronics would only function intermittently if held at extreme angles.

Soldering right components with rigid solder to a flexible PCB was not a good idea.

I had to rewire everything, including a new Arduino Nano to a new rigid PCB which I cut down to size. Luckily, everything still fit inside the housing.

In the process of rewiring components to a new (rigid) PCB.

In all previous steps, the Nano accepted code upload fine and functioned as designed. Other people in the class had run into issues with uploading to the Nano and electrical faults, but I had yet to experience these issues until everything (including the new Nano) was soldered together. I had tested this Nano with the Blink sketch and my code prior to transferring everything from a breadboard to the PCB and it performed as expected, but once I had finished soldering and needed to reupload my code, it failed.

Fortunately, I was able to upload the code with a Windows PC. Afterwards, I assembled the base.

Photos

A pulsing blue pattern indicates that it’s currently drier than the set threshold.

To adjust the threshold humidity, spin the top dial. If the relative humidity is at or above the threshold, a green pattern is visible.

Discussion
Response

Could there be a component that controls the ticking sound (ie on/off)?”

I can see how a clicking sound would be undesirable in certain situations, but the piezoelectric buzzer used is so quiet (especially when contained in the enclosure) that it probably won’t be an issue. It’s quieter than the Home button sound effect in iPhone 7 and 8.

So one question I have is it’s a good idea for this to be something that you have to remove the humidifier to see/interact with? Would it be better if you could see the lights when the humidifier is docked?”

In theory one would not frequently adjust the threshold humidity once it’s set and the humidifier is quite small and light (about the size of a water bottle) that it’s easy to take off, adjust the threshold on the base, and put back on top. The base of the humidifier is clear plastic so the LEDs are visible when on.

Self critique

I’m quite relieved that this project eventually worked after several significant hardware failures and challenges. I wanted to make the humidifier base to the best of my ability so that I could include it in my portfolio, so I spent considerable time on programming, design, and fabrication. As an early iteration, the physical design of the base could use many refinements – I already have some sketches and CAD files of forms I think would work better – but I’m glad I eventually reached the goal of having a physical base that functions as envisioned.

What I learned

In the future I will definitely avoid flexible PCBs in my projects.

Balancing objectives for the code took quite some time. For example, I wanted to use a smoothing function tying the ambient light sensor (ALS) readings to the LED outputs, but the particular array implementation I was trying requires multiple readings in order to establish an average that reflects current conditions. Normally, this wouldn’t be an issue since the ALS readings happen very quickly, but because of the sin function used to control LED pulsing, ALS readings were delayed and thus the LEDs would only very slowly increase brightness when the device was first plugged in.

When depending on many different manufactured parts from many different vendors, sometimes a part does not function or look as described. I had a few of these obstacles pop up over the course of this project and had to redesign around those constraints. While not ideal, these issues pop up quite frequently during the manufacturing and assembly of actual products (unless every component is designed and manufactured in-house). As an industrial design student, handling these issues provided a valuable learning experience. I can attest that “hardware is hard.”

Next steps

The most immediate next step is to refine the design of the enclosure to be more cohesive. I’ve already started sketching and modeling more developed forms, continuing to think about materials and finishes that would serve the product well.

Now that I know how all the electronic components inside are wired and function, I could also, in the future, design a custom PCB that would be mounted in the housing and connect all the components together. This would do away with messy and unreliable wires jumping from a small rectangular board to components situated all around and serve to make the product more robust and refined internally.

Finally, while the NeoPixel Ring functioned very well, it is sold in only a few sizes. Thus, while the outer diameter of the base measures 5″, the LED ring is quite a bit smaller. I would have liked for the LEDs to be closer to the outer edge of the enclosure. With a custom assembly of LEDs this would theoretically possible, but I’ve already had so much trouble wiring existing components that creating an entirely new and complex electronic part would be something very far down the road.

Renders of a new design.

Schematic

Code
/* Humidifier Base
 *  
 * Ian Shei
 * 
 * Description: Implements functionality for a custom-designed humidifier base
 * that reads the relative humidity in a room. If the humidity is lower than the
 * set threshold, LEDs mounted near the top of the base will pulse to notify the 
 * user to turn on the humidifier – the faster the pulsing, the drier then air. 
 * One can set the threshold humidity by turning the dial forming the top of the 
 * base, with visual and auditory feedback from the LEDs and a speaker, 
 * respectively. Once the relative humidity meets the threshold, the LEDs will 
 * turn green for a few seconds then turn off, indicating that the humidifier no 
 * longer needs to be on.
 * 
 * Adafruit NeoPixel implementation adapted from Adafruit_Neopixel library.
 * Available via https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation.
 * 
 * DHT sensor implementation adapted from Adafruit DHT sensor library. Available
 * via https://learn.adafruit.com/dht/using-a-dhtxx-sensor.
 * 
 * mapfloat function adapted from user "skumlerud" on forum.arduino.cc. Available
 * via http://forum.arduino.cc/index.php?topic=3922.0.
 * 
 * Smoothing implementation adapted from David A. Mellis & Tom Igoe's example at 
 * http://www.arduino.cc/en/Tutorial/Smoothing.
 * 
 * millis() rollover implementation adapted from James Lewis' example at 
 * https://www.baldengineer.com/arduino-how-do-you-reset-millis.html.
 * 
 * Piezoelectric tick implementation adapted from user "el_supremo" on
 * forum.arduino.cc. Available at https://forum.arduino.cc/index.php?topic=299032.0.
 * 
 * LED pulsing implementation adapted from MikeGrusin's Sparkfun tutorial.
 * Available at https://www.sparkfun.com/tutorials/329.
 * 
 * Encoder implementation adapted from Paul Stoffregen's Quadrature Encoder Library
 * for Arduino. Available at https://github.com/PaulStoffregen/Encoder.
*/

#include "DHT.h"
#include "math.h"

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

// setting up encoder
#include <Encoder.h>
long previous; // previous position reading of encoder
long current; // current position reading of encoder
int ledIndex = 11; // 24 LEDs in NeoPixel Ring. By default, the threshold humidity is set at 50% (hence index of 11).
int thresholdLed = 0; // thresholdLed takes value of ledIndex when ledIndex is changed
Encoder myEnc(2, 3);

// setting up NeoPixel, DHT sensor, ambient light sensor, speaker pins
#define STRIPPIN 5 // NeoPixel Ring
#define DHTPIN 11
#define DHTTYPE DHT22
#define LIGHTSENSORPIN A0
#define TICK_PIN 7

Adafruit_NeoPixel strip = Adafruit_NeoPixel(24, STRIPPIN, NEO_GRBW + NEO_KHZ800);

DHT dht(DHTPIN, DHTTYPE);

float pulseIncrement = 0; // adjusts the speed at which LEDs pulse
bool editMode = false; // currently editing threshold or not
bool greenOn = false; // is the green LED pattern (indicates relative humidity has met threshold) on?
bool afterTrigger = false; // if relative humidity is at or higher than threshold, green LED pattern will show and afterTrigger will be true
float h; // humidity

// mapfloat functino returns float values
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

unsigned long markMillis = 0; // marks time at which last input has taken place

// setting up smoothing of ambient light sensor (ALS) readings
const int ambientNumReadings = 100;
int ambientReadings[ambientNumReadings];
int ambientReadIndex = 0;
int ambientTotal = 0;
float ambientAverage = 0;
float percent_ambient;

void setup() {
  Serial.begin(9600);

  dht.begin();
  h = dht.readHumidity();
  
  strip.begin();
  strip.setBrightness(50);
  strip.show(); // Initialize all pixels to 'off'

  pinMode(LIGHTSENSORPIN, INPUT);

  // ALS smoothing
  for (int thisAmbientReading = 0; thisAmbientReading < ambientNumReadings;
  thisAmbientReading++) {
    ambientReadings[thisAmbientReading] = 0;
  }
}

void loop() {
  // ALS smoothing
  ambientTotal = ambientTotal - ambientReadings[ambientReadIndex];
  ambientReadings[ambientReadIndex] = analogRead(LIGHTSENSORPIN);
  ambientTotal = ambientTotal + ambientReadings[ambientReadIndex];
  ambientReadIndex = ambientReadIndex + 1;
  
  if (ambientReadIndex >= ambientNumReadings) {
    ambientReadIndex = 0;
  }
  ambientAverage = abs((ambientTotal / ambientNumReadings) / 1023.0);
  percent_ambient = abs(analogRead(LIGHTSENSORPIN) / 1023.0);
  
  float in, out; // in defines the starting and ending position of the LED pulsing sin function seen later; out is LED brightness output

  unsigned long currentMillis = millis(); // current time
  
  // encoder implementation
  previous = current;
  current = myEnc.read() / 3.33333333;
  
  if(previous != current) { // if encoder position has changed enter threshold edit mode
    editMode = true;
  }
  
  if(editMode) {
    for (int n = 0; n < strip.numPixels(); n++) {
      strip.setPixelColor(n, 0, 0, 0, 0);
    }

    if(previous > current) {
      if (ledIndex < 23) {
        ledIndex++;
          tone(TICK_PIN, 20000, 1); // tick sound effect whenever an LED turns on or off
        
        markMillis = currentMillis;
      }
    }
    
    if (previous < current) {
      if (ledIndex > 0) {
        ledIndex--;
          tone(TICK_PIN, 20000, 1);
        
        markMillis = currentMillis;
      }
    }
    
    for (int i = 0; i <= ledIndex; i++) {
      strip.setPixelColor(i, 0, 0, ambientAverage * 255, 0); 
    }
    strip.show();
    
    for (int i = ledIndex; i >= 0; i--) {
        strip.setPixelColor(i, 0, 0, 0, 0); 
    }
    thresholdLed = ledIndex;
    
    if((unsigned long)(currentMillis - markMillis) >= 4000) { // if it has been 4 seconds since last encoder input, exit edit mode
      markMillis = currentMillis;
      editMode = false;
    }
    
    greenOn = false; // green LED pattern does not show in edit mode
    afterTrigger = false; // resets so that if relative humidity is at or higher than threshold after editing, LED pattern can show after exiting edit mode
  }

    float t = dht.readTemperature(); // temperature readings for possible future extension
    h = dht.readHumidity();
    float f = dht.readTemperature(true);
    
  if(!editMode) {
    if (isnan(h) || isnan(t) || isnan(f)) {
      Serial.println(F("Failed to read from DHT sensor!")); // outputs to serial in case DHT sensor fails or is disconnected
      return;
    }
    
    if(h <= map(ledIndex, 0, 23, 0, 100) && h >= 0) { // if humidity is lower than threshold, pulse LEDs
      pulseIncrement = mapfloat(h, 0, map(ledIndex, 0, 23, 0, 100), 0.015, 0.005); // the lower the relative humidity is, the faster the LEDs will pulse
      
      for(in = 1.5 * M_PI; in < 3.5 * M_PI; in = in + pulseIncrement) // sin(1.5pi) = -1, period ends at 3.5 pi such that LEDs are off -> on -> off
      {
        out = sin(in) * 127.5 + 127.5; // sin extrema are -1 and 1 such that out = [0, 255] corresponding to LED brightness output
        
        if (percent_ambient <= 0.05) { // if it is really dark (e.g. ALS reading = 0), manually control LED brightness so that LED output is still visible
          for (int n = 0; n < strip.numPixels(); n++) {
            strip.setPixelColor(n, 0, 0, out * 0.05, 0);
          }
        }
        
        else {
          for (int n = 0; n < strip.numPixels(); n++) {
            strip.setPixelColor(n, 0, 0, percent_ambient * out, 0);
          }
        }
        strip.show();
      }
      greenOn = false; // green LED pattern does not show in LED pulse mode
      afterTrigger = false; // resets so that if relative humidity is at or higher than threshold after editing, LED pattern can show
    }
    
    else if (!greenOn){ // if relative humidity >= threshold, show green LEDs
      for(in = 1.5 * M_PI; in < 2.5 * M_PI; in = in + 0.005) {
        out = sin(in) * 127.5 + 127.5;
      
        for (int n = 0; n < strip.numPixels(); n++) {
          strip.setPixelColor(n, 0, percent_ambient * out, 0, 0);
        }
        strip.show();
      }
      markMillis = currentMillis;
      Serial.println("greenOn");
      greenOn = true; // green pattern is on
    }

    // if green pattern has shown already, turn off LEDs until relative humidity no longer meets or exceeds threshold
    if(!afterTrigger && greenOn && (unsigned long)(currentMillis - markMillis) >= 4000 && h > map(ledIndex, 0, 23, 0, 100) && h >= 0) {
      for(in = 2.5 * M_PI; in < 3.5 * M_PI; in = in + 0.005) { // sin(2.5pi) = 1, sin(3.5pi) = -1 such that LEDs dim to 0 along sin curve
        out = sin(in) * 127.5 + 127.5;
      
        for (int n = 0; n < strip.numPixels(); n++) {
          strip.setPixelColor(n, 0, percent_ambient * out, 0, 0);
        }
        strip.show();
      }
      afterTrigger = true;
    }
  }
}

 

]]>
Fix Your Posture ! https://courses.ideate.cmu.edu/60-223/s2019/work/fix-your-posture/ Fri, 22 Mar 2019 10:24:42 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6918 Overview

I created a posture sensor to help me fix my back. It’s a light signal bracelet that tells you at which parts of your back is not in the correct posture.

 

              Velcro bracelet to wrap around wrist

The different lights will signal which part of the back is bad by turning into a red light.

Flex sensors are meant to be body taped to the back and the rest of the arduino is housed within a bag. It would have been better if there was some way to implement the flex sensors onto the back without having to tape because it does not work as well and it is uncomfortable.

This bag was made to clip onto the belt loops of your pants and the wires that exited the bag, to be taped onto your back and to the bracelet, would be hidden underneath your clothes. However, it was hard to put everything in the bag because wires would come out from the breadboard and every time I did try to put the arduino components in the bag, something always malfunctioned.

Because of my poor housing I tried to replicate how the device would work by taping the flex sensors to the back of a paper human cut out.

Process

Started off with a simple circuit and code from a tutorial that controlled the brightness of an a single LED light

Moved onto the light strips so I could later implement it into the rectangular watch. 

 

 I had difficulty trying to smooth the coding for the lights so that when the posture was good it would be green, but then progress to red depending on how bad the posture was.

Still having troubles with the sensitivity of the sensors in this video. This step is also when I decided I wanted to sense multiple parts of my back so I did two flex sensors sending data to different LED lights on the strip.

 

Discussion

This project got me really excited because I had a desire to make something that was more personal for me and could benefit me. During class critique, the guest person brought up major points to my design, which transitioned my thinking of this project from a school assigned project to a project creating an everyday object. He pointed out how it would be troublesome to tape to the sensors to one’s back everyday as well as having to hide the wires underneath the clothes. However, my fellow classmates liked my design (despite the lack of usability) of the bag, and brought up points on how it was easier to use for everyday life.

Bumps I encountered during this project were mainly the housing. If I were able to change how I thought during the process, I would have thought more on how to secure the wires better, which in turn could have caused the bag housing to work. Another problem that occurred was within the coding, where for some reason the flex sensors were reading at different values and I had just assumed that they were the same. To elaborate, one of them had a range from 700-900 while the other had a range from 200-500. Since I did not print the value of the second sensor, I did not know why the numbers I inputed into my code was not working, until after I started reading the second sensor. It taught me a valuable lesson to always print your values.

I would really like to go back to work on this project if I had time. Most likely, I would heat wrap the wires together to prevent having multiple wires tangle as well as using a different type of breadboard. Another thing I would still need to consider is how to attach the sensors to my back because even when I asked my table about any possible solutions, none were spoken. Unlike the double transducer where I struggled more with the coding because of my lack attention to the schematic and code structure, this project’s downfall was my physical housing. This was due to less planning on the physical and more focus on the coding.

Schematic

Code

// Fix Your Posture!
// Nina Yoo
/*Description : This project mounts two flex sensors onto one's back 
and notifies the user of their bad posture by emitting a red light 
from the LED*/

#include <PololuLedStrip.h>

// Create an ledStrip object and specify the pin it will use.
PololuLedStrip<5> ledStrip;

// Create a buffer for holding the colors (3 bytes per color).
#define LED_COUNT 60
rgb_color colors[LED_COUNT];
//Constants:
const int flexPin2 = A1;
const int flexPin = A0; //pin A0 to read analog input
const int NUM_READINGS = 10;
const int NUM_READINGS2 = 10;
//Variables:
int value; //save analog value
int value2;
int readings[NUM_READINGS];
int readIndex = 0;
int total = 0;
int average = 0;
int readings2[NUM_READINGS2];
int readIndex2 = 0;
int total2 = 0;
int average2 = 0;

void setup() {
  pinMode(flexPin, INPUT);
  pinMode(flexPin2, INPUT);
  Serial.begin(115200);
  for (int thisReading = 0; thisReading < NUM_READINGS; thisReading++) {
    readings[thisReading] = 0;
  }
  for (int thisReading2 = 0; thisReading2 < NUM_READINGS2; thisReading2++) {
    readings2[thisReading2] = 0;
  }

}

void loop() {

  total = total - readings[readIndex];
  readings[readIndex] = analogRead(flexPin);
  total = total + readings [readIndex];
  readIndex = readIndex + 1;
  if (readIndex >= NUM_READINGS) {
    readIndex = 0;
  }
  average = total / NUM_READINGS;
  Serial.println(average);
  total2 = total2 - readings2[readIndex2];
  readings2[readIndex2] = analogRead(flexPin2);
  total2 = total2 + readings2[readIndex2];
  readIndex2 = readIndex2 + 1;
  if (readIndex2 >= NUM_READINGS2) {
    readIndex2 = 0;
  }

  average2 = total2 / NUM_READINGS2;
  Serial.println(average2);
  value = analogRead(flexPin); 
  Serial.print("flex1 = ");          
  Serial.print(value);
  //Map value 0-1023 to 0-255
  value = map(value, 700, 900, 0, 255); 
  Serial.print("flex1 = ");          
  Serial.println(value);
  analogWrite(ledPin, value);          
  //Small delay
  value2 = analogRead(flexPin2);
  Serial.print("\tflex2 = "); 
  Serial.print(value2);
  value2 = map(value2, 580, 850, 0, 255);
  Serial.print("\tflex2 = "); 
  Serial.println(value2);
  analogWrite(ledPin, value2);
  colors[0] = rgb_color(value, 255 - value, 10); 
  // Write the colors to the LED strip.
  ledStrip.write(colors, LED_COUNT);
  colors[2] = rgb_color(value2, 255 - value2, 10);
  ledStrip.write(colors, LED_COUNT);
  
}

 

]]>
Back It Up, Boy! https://courses.ideate.cmu.edu/60-223/s2019/work/back-it-up-boy/ Thu, 21 Mar 2019 05:11:38 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6861 “Back It Up, Boy!” is an assistive device to help users backup from their laptop screens when they unknowingly become too close after leaning in for a period of time.

Overview of hat

Front View

Inside of hat

Sensor Detail Shot

Motor attached inside

Seated – Good Form

Seated — Not optimal, but tolerable form

Seated — Awful form

 

Process

Playing around with the both the Haptic Motor Driver and Distance Sensor. During this process, I spent about an hour or so trying to figure out why my sensor was not working, only to realize I had downloaded the wrong library. 

Using wrong libraries

Once I got my libraries sorted, and all my components (haptic motor, sensor, motor, and code) up and running, I moved onto soldering my parts together.

Soldering Haptic Driver

The aftermath of soldering components. In this picture, solidcore wires were used, however, I went back and resoldered each of the components using stranded wires.

After running into issues with my prototype after uploading it onto the Arduino Nano, I had to run, what seemed like a billion tests, with a Voltmeter to see voltage among other points of contact within the circuit to see if it was a matter of issue with the rest of the components. 

 

I do not have eight arms to hold up all components and wires of my project, but luckily this contraption did.

I found that at certain points, the step down resistor created an undesired Voltage output, and a Voltage different than what was actually received at other components as well. So, to test out if it was a matter of poor soldering and/or faulty parts, I resoldered new step downs to new battery cases three times.

Soldering step downs, battery cases, and wires three times

Concluding that there was a matter with the Arduino Nano (and later, the Haptic Motor Driver) I had to shift over gears to using an Arduino Uno again. As a result, I also had to shift over my battery sources, from a two 3V battery disks, to a 9V battery pack, in order to properly charge the Arduino.

Switching out the batteries.

As a result of this, I had to take a 180 degree turn from my initial idea with the project, fully embracing all the bulky components and wires of my parts.

Initial run: Attaching the sad, bulky prototype onto a hat. Not what I envisioned at all for my final product, as most of the parts that I had intended to use broke on me, and had to be exchanged out for larger parts.

Discussion

responses:
  • Adding more sensors to the project.
    • “I think that adding sensors will definitely help make it more helpful in most scenarios”
    • “I wonder if a baseball cap is the best choice because of how the brim might cause you to orient the sensor”

I agree with these responses from my classmates in that having one sensor orientated in one direction would limit the user’s ability to be alerted when learning in too close to their work. It would be an interesting exploring the different types of sensors / alerts that could arise when a user, say, is leaning too much over to one side (for ex. Having their head on their hand and thus slumping).

  • Adding different types of feedback.
    • “Did you consider indicators other than vibration?”
    • “Maybe a dim light would improve the project to give some extra notice to the person. Of course, a light could also be more distracting than vibration, so it would need some experimentation”

I considered the multitude of other possible indicators that one could receive, the second most popular option namely being a visual (ie. light). However, I found that this would possibly be easier to ignore, as light source from a computer screen can already take up the visual stimulation we receive. Touch, on the other hand, is a sense that is not usually taken note of and/or being utilized when we work at a desk. After researching the different types of sensors, I concluded that haptic feedback was the best in terms of allowing a user to obtain a sense of spatial awareness.

self-Critique

Personally, I am not satisfied with the end product of this project, mainly because of how everything seemed to perfectly fall apart one after the other once I began soldering things onto the final Arduino hardware. Due to these struggles, not only was I unable to execute my envisioned final project, I was not able to explore how I could further refine the interactions (such as vibrations), and or other nuances and details that could further enhance my product, because I was instead too focused on how to actually get my components to work together again.

Main Takeaways + Lessons

Nonetheless, I am grateful for how much I was able to learn throughout the process of this project, as I had not been able to be the sole designer and creator of a physical computing project before. I became a lot more comfortable with physically manipulating parts and wires through soldering, stripping wires, and other rapid prototyping. In addition, I was able to expose myself to and learn about a whole new library of parts and their libraries, and was able to discover the multitude of capabilities each had, as well as the differences between the types of communications they were able to conduct (namely, the concept of I2C sensors, or, Inter-Integrated Circuits, and how they are able to communicate across the Arduino using similar pins, because of the different addresses each part is assigned)

NEXT Steps

Moving forwards, I would be really interested in possibly exploring the idea of creating a seamless wearable device again. Although this would require better parts and components, I think it would be a useful application in the future.

Technical Information

Schematic

Schematic

Code
/*Vicky Zhou
 * This code uses an I2C Sensor to detect distance
 * which then instigates vibration sequence
 * that change pattern depending on relative distance 
 */

#include <Wire.h>
#include "Adafruit_VL53L0X.h"

const int MOTOR = 9; //analog output pin for HMD 

Adafruit_VL53L0X lox = Adafruit_VL53L0X(); //I2C sensor

void setup() {
  Serial.begin(115200);
//  drv.begin();

  //give a slight delay for serial port to open for native USB device 
  while (! Serial) {
    delay(5);
  }

  //Serial print to see if VL53L0X is working 
  Serial.println("Adafruit VL53L0X test");
  if (!lox.begin()) {
    Serial.println(F("Failed to boot VL53L0X"));
    while(1);
  }

  pinMode(MOTOR, OUTPUT); 
  digitalWrite(MOTOR, LOW); 
}

void loop() {
  VL53L0X_RangingMeasurementData_t measure; //range measurement

  Serial.print("Reading a measurement... ");
  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!

  if (measure.RangeStatus != 4) { //phase failures have incorrect data
    Serial.print("Distance (mm): ");
    Serial.println(measure.RangeMilliMeter);
  } else {
    Serial.println(" out of range ");
  }

  //if distance from sensor is greater than 250 (far) 
  //turn motor off
  if (measure.RangeMilliMeter >= 250){
    Serial.println("Off");
    analogWrite(MOTOR, 0); //off sequence
    delay(50);
  }
  //if distance from sensor is between 150 and 250 (mid range)
  //turn motor on to sequence B 
  else if (measure.RangeMilliMeter >= 150 && measure.RangeMilliMeter < 250){
    Serial.println("Sequence B");
    analogWrite(MOTOR, 130); //medium sequence
    delay(50); 
  }
  //if distance from sensor is less than 150 (close) 
  //turn motor on to sequence C 
  else {
    Serial.println("Sequence C");
    analogWrite(MOTOR, 255); //fast sequence
    delay(50);
  }
}

 

]]>
Word Scramble Alarm Clock https://courses.ideate.cmu.edu/60-223/s2019/work/word-scramble-alarm-clock/ Wed, 20 Mar 2019 13:29:59 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6844 This alarm clock won’t stop beeping until you unscramble a word.

Demo Video

Using the keypad to enter letters

Close-up of the LCD screen

An early prototype, with a keypad and potentiometer. I initially wanted to do a sort of “bomb defusal” alarm clock that involved cutting wires, but chose instead to go with the keypad as a more elegant reusable puzzle.

Here I began to solder the components together. At this point I had added a speaker and real time clock. I chose to switch the potentiometer for a detented rotary encoder, which was more intuitive for navigating menus and included a button.

Detail of the connection between the real time clock and the bit of protoboard I used for power rails.

Here I began mounting pieces into the box. I originally wanted to lasercut an acrylic box for the project but I spent more time than anticipated troubleshooting the real time clock, speaker, and rotary encoder, and ended up having to go for a simpler enclosure.

The internals of the box. I mounted everything to the top so that I could still open and close the box to change the battery.

Response to in-class crit

  • “Maybe could laser cut something for a more permanent solution.”
  • “Very annoying and effective alarm that will probably wake you up better since you need to actually think for this puzzle which uses brain power.”
  • “It might be too hard when you wake up. I think you’ll end up unplugging it rather than solving the puzzle.”

As anticipated, a lot of the responses were that the enclosure was unrefined and a lasercut box would be more polished. One thing I didn’t consider was how to prevent methods of cheating, such as unplugging the Arduino or destroying the speaker. A sturdy box that’s screwed shut would be a good solution. You could still change the battery when needed, but it would take you several minutes to unscrew and open the box. People did like the concept though and said that the alarm and puzzle itself appeared very functional.

Self critique

I’m happy with how the project came out functionally. The menus and the puzzle are user-friendly and easy to interact with. My main goal was to have a hard puzzle that would be truly challenging and force me to have to think, which I satisfied. I wish I budgeted more time toward making a better/smaller/sturdier enclosure.

What I learned

Getting several components to interact took probably 3 times as long as I expected. I knew that all the components I was using had free libaries that I could use, but actually digging through the documentation and testing the behavior to get what I wanted was not always straightforward. There was lots of troubleshooting for each component, and some places where I had to check multiple places to see where my issue was. I got hung up for a while because one of my wires came out of the Arduino and my LCD display spontaneously stopped working.

Next steps

The project is functionally all there, but as I said before, I’d build a new enclosure for it that could only be taken apart with a screwdriver and lots of patience.

#include <Encoder.h>
#include  <Wire.h>
#include  <LiquidCrystal_I2C.h>
#include  <Keypad.h>
#include "DS3231.h"

const int NUM_WORDS = 32;

String words[] = {
  "change",
  "again",
  "animal",
  "begin",
  "began",
  "black",
  "machine",
  "field",
  "final",
  "ocean",
  "behind",
  "decide",
  "common",
  "check",
  "among",
  "dance",
  "engine",
  "million",
  "middle",
  "child",
  "climb",
  "office",
  "clean",
  "blood",
  "decimal",
  "imagine",
  "chief",
  "clock",
  "block",
  "chance",
  "claim",
  "chick"
};

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'a','b','c','d'},
  {'e','f','g','h'},
  {'i','j','k','l'},
  {'m','n','o','p'}
};

//byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
//byte colPins[COLS] = {12, 8, 7, 6}; //connect to the column pinouts of the keypad
byte rowPins[ROWS] = {2, 3, 4, 5};
byte colPins[COLS] = {6, 7, 8, 12};

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

RTClib RTC;
Encoder knob(11, 9);

String currentWord, currentScramble;
String enteredSoFar;

String shuffle(String s)
{
  String result = String(s);
  for (int i = s.length() - 1; i > 0; i--) {
    int j = random(0, i + 1);
    char temp = result[i];
    result.setCharAt(i, result[j]);
    result.setCharAt(j, temp);
  }

  Serial.print("Original string: "); Serial.println(s);
  Serial.print("Shuffled string: "); Serial.println(result);

  return result;
}

void resetWord()
{
  currentWord = String(words[random(0, NUM_WORDS - 1)]);
  currentScramble = shuffle(currentWord);

  enteredSoFar = String("");
}

void setup()
{
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);

  randomSeed(analogRead(0));

  pinMode(13, INPUT);

  resetWord();
}

enum State {
  REGULAR_CLOCK,
  SET_ALARM_H,
  SET_ALARM_M,
  CANCEL_ALARM,
  ALARM_WAITING,
  ALARM_RINGING,
  CONGRATS
};

State currentState = REGULAR_CLOCK;
int alarmH = 0;
int alarmM = 0;

long lastUpdateTime = 0;
const int REFRESH_INTERVAL = 500;

long setAlarmTime = 0;
const int CANCEL_INTERVAL = 5000;

bool oldButtonState = true;
bool newButtonState;

bool cleanButtonPress() {
  newButtonState = digitalRead(13);
  bool returnVal = !newButtonState && oldButtonState;
  oldButtonState = newButtonState;
  return returnVal;
}

void loop()
{
  switch(currentState) {
    case REGULAR_CLOCK:
      {
      DateTime now = RTC.now();
        
      if (millis() - lastUpdateTime > REFRESH_INTERVAL) {
        lcd.clear();
        lcd.print(now.hour(), DEC);
        lcd.print(":");
        byte m = now.minute();
        lcd.print(m < 10 ? "0" : "");
        lcd.print(m, DEC);
        lastUpdateTime = millis();
      }

      if (cleanButtonPress()) {
        currentState = SET_ALARM_H;
      }
      }
      break;
    case SET_ALARM_H:
      alarmH = (knob.read() / 4) % 24;
      while(alarmH < 0) alarmH += 24;

      if (millis() - lastUpdateTime > 100) {
        lcd.clear();
        lcd.print("Hour:");
        lcd.print(alarmH);
        lcd.print(":");
        lcd.print(alarmM < 10 ? "0" : "");
        lcd.print(alarmM);
        lastUpdateTime = millis();
      }

      if (cleanButtonPress()) {
        currentState = SET_ALARM_M;
      }
    
      break;
    case SET_ALARM_M:
      alarmM = (knob.read() / 4) % 60;
      while(alarmM < 0) alarmM += 60;

      if (millis() - lastUpdateTime > 100) {
        lcd.clear();
        lcd.print("Minute:");
        lcd.print(alarmH);
        lcd.print(":");
        lcd.print(alarmM < 10 ? "0" : "");
        lcd.print(alarmM);
        lastUpdateTime = millis();
      }

      if (cleanButtonPress()) {
        currentState = CANCEL_ALARM;
        setAlarmTime = millis();
      }
    
      break;
    case CANCEL_ALARM:
      if (millis() - lastUpdateTime > REFRESH_INTERVAL) {
        lcd.clear();
        lcd.print("Set:");
        lcd.print(alarmH);
        lcd.print(":");
        lcd.print(alarmM < 10 ? "0" : "");
        lcd.print(alarmM);
        lcd.setCursor(0, 1);
        lcd.print("Cancel? (");
        lcd.print(((CANCEL_INTERVAL - (millis() - setAlarmTime)) / 1000) + 1);
        lcd.print(")");
        lastUpdateTime = millis();
      }

      if (cleanButtonPress()) {
        currentState = REGULAR_CLOCK;
      }

      if (millis() - setAlarmTime >= CANCEL_INTERVAL) {
        currentState = ALARM_WAITING;
      }
      
      break;
    case ALARM_WAITING:
      {
      DateTime now = RTC.now();
        
      if (millis() - lastUpdateTime > REFRESH_INTERVAL) {
        lcd.clear();
        lcd.print(now.hour(), DEC);
        lcd.print(":");
        byte m = now.minute();
        lcd.print(m < 10 ? "0" : "");
        lcd.print(m, DEC);
        lastUpdateTime = millis();

        lcd.setCursor(0, 1);
        lcd.print("Alarm at ");
        lcd.print(alarmH);
        lcd.print(":");
        lcd.print(alarmM < 10 ? "0" : "");
        lcd.print(alarmM);
        lastUpdateTime = millis();
      }

      if (now.hour() == alarmH && now.minute() == alarmM) {
        currentState = ALARM_RINGING;
      }
      
      }
      break;
    case ALARM_RINGING:
      {
      if (millis() % 1000 < 500) tone(10, 600);
      else noTone(10);
      
      int i = (knob.read() / 4) % currentScramble.length();
      while (i < 0) i += currentScramble.length();

      if (millis() - lastUpdateTime > REFRESH_INTERVAL) {
        lcd.clear();
        lcd.print(String(i + 1) + ": " + currentScramble[i] + " " + enteredSoFar);
        lastUpdateTime = millis();
      }
    
      char key = keypad.getKey();
    
      if (key) {
        enteredSoFar.concat(key);
        if (enteredSoFar != currentWord.substring(0, enteredSoFar.length())) {
          lcd.clear();
          lcd.print("WROOOOOOONG");
          enteredSoFar = String("");
          delay(700);
        } else if (enteredSoFar == currentWord) {
          resetWord();
          currentState = CONGRATS;
        }
      }
      }
      break;
    case CONGRATS:
      lcd.clear();
      lcd.print("Hooray!");
      lcd.setCursor(0,1);
      lcd.print("You're awake");
      delay(2000);
      currentState = REGULAR_CLOCK;
      break;
  }
}

 

 

]]>
The Reminder https://courses.ideate.cmu.edu/60-223/s2019/work/the-reminder/ Wed, 20 Mar 2019 12:44:50 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6424 Brief Explanation

A physical reminder with customizable timing options ranging from 28 seconds to 24 hours.

Images

 

top of the reminder which displays the current timer setting

The switch on top activates a sound alarm if it is turned on. The reminder uses a 9V battery to power it.

(usage example – reminding to drink water)

Place next to object you want to be reminded about, and set the timer to the desired reminder frequency.

Once the timer runs out, the alarm will sound (quiet mode uses LEDs as shown in picture but you can have it use sound as well – plays a little song instead of lighting up). After removing and returning the water bottle from in front of the sensors, the timer resets.

If the water bottle is removed before the timer runs out and the alarm goes off, the timer is reset.

Process Images and Review

I began by attempting to use HC-06 Bluetooth modules to be able to communicate between a sensor and the actual “reminder” which would sound the alarm. That way the sensor could be a few feet from you but the alarm would go off right next to you. Tried switching to the nRF24L01 but the devices wouldn’t connect with each other

Tried to use the Arduino nano for the reminder but couldn’t get it to work reliably so I decided to use the regular Arduino. I was trying to create a more compact design, but since this is the first iteration, I thought I should focus more on the functionality

This was the separate sensor portion from when I was attempting to use the Bluetooth chips to communicate between two Arduinos

Got rid of the bluetooth chips and made it compact by using only one Arduino. Although not my original design, I thought the bluetooth chips would be too finicky and unstable, so I decided to eliminate them entirely.

I added an LCD screen that shows the number of hours/minutes/seconds that the timer is currently set to. This was the final prototype before I started soldering and creating the housing.

Everything all soldered up

Discussion

Quotes from crit:

  • “I like that there are a lot of parts that you can play with on the box and it looks nice”
  • “Good form factor and clean presentation.”
  • “I like how you have the knobs and the smoky arcyllic; I also like the quiet and sound setting.”
  • “I like the series of potentiometers that can be used to customize the interaction. Also, the box is a nice form factor besides the battery being outside.”

Reflection:

I think that people enjoyed the aesthetic of the arctic frost acrylic housing around the circuitry for this project. It created a slight techy view into the inner workings of the box, while also making the LED usage inside the box look really cool. I think that one of the components that I should have worried about was the battery. I wanted this project to not be constricted by having to be plugged in but didn’t think far enough ahead, in terms of housing, for the battery’s location.

As for my own goals that I set for myself regarding the final outcome of this project, I feel good about my final product, even though I encountered a few hiccups along the way. I wanted to use the Bluetooth chips because they would allow some proximity sensor to detect the lack of motion then send that data to another chip that was in my pocket, so I would be able to receive the alarm even if I was in the other room.

I got a bit fixated on the usage of the Bluetooth in my project when I could have been creating something that would do essentially the same things I wanted it to do without Bluetooth with more simplicity and with fewer errors. After seeing all of the other projects, I really think that my project and Nathan’s project (see next paragraph) could be brought together and used to create a really effective reminder. The adjustable timing from my project would have been great with his box. If each slot had a timer for when you’d have to have it back in the box by, the box could remind you to either take things out of the box and/or remind you to put things back in the box. This double functionality could be used to help remind you of things to remember to grab in the morning and for reminding you to replace your things at night.

Nathan’s project – a box with compartments and it the alarm would go off if there was something missing from its area. He used this to organize all of his jewelry because he has quite a bit. Most were rings and bracelets, so I can see how it would be easy to lose them.

Schematic

 

 

Code

/*
   Eric Mendez
   emendez
   Project 2: "The Reminder"

   Description: Detects the lack of motion of an object for
   programmable time intervals to help remind the user to do 
   a certain task at each of those time intervals.

   Inputs: switch(quiet mode), ultrasonic ranger(distance measuring),
           2 potentiometers (hour and min)
   Outputs: speaker, LED strip, LCD screen
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <NewPing.h>
#include <TimerFreeTone.h>
#include <PololuLedStrip.h>

// set up devices and set const variables
LiquidCrystal_I2C lcd(0x3F, 16, 2);

#define TRIGGER_PIN  9  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     8  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

// Create an ledStrip object and specify the pin it will use.
PololuLedStrip<6> ledStrip;

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


const int QUIET_SWITCH = 7;
const int LED = 6;
const int SPEAKER = 5;
const int POT_PIN_HOUR = A1;
const int POT_PIN_MIN = A2;

const unsigned long SCALE_POT = 86400000;
const unsigned long HOUR_MS = (unsigned long)60*(unsigned long)60*(unsigned long)1000;
const unsigned long MIN_MS = (unsigned long)60*(unsigned long)1000;
const unsigned long SEC_MS = (unsigned long)1000;

// set up global changable variables
unsigned long reset_threshold = 0;
unsigned long run_time = 0;
unsigned long prev_time = 0;
unsigned long led_start_time = 0;

bool led_on = false;

int quietMode = 0;
int sonar_zeros_in_row = 0;

// note intervals for song
int a3 = 220;
int Asharp3 = 233;
int b3 = 246;
int c4 = 261;
int csharp4 = 277;
int d4 = 293;
int dsharp4 = 311;
int e4 = 329;
int f4 = 349;
int fsharp4 = 369;
int g4 = 392;
int gsharp4 = 415;
int a4 = 440;
int asharp4 = 466;
int b4 = 493;
int c5 = 523;
int eighth = 125;
int quarter = 250;
int half = 500;
int melody[] = { c5, b4, a4, g4, g4, a4 };
int duration[] = {(quarter + half), quarter, half, half,
                  (half + eighth), (half + half)
                 };

void setup()
{
  Serial.begin(9600);

  // start LCD screen
  lcd.init();
  lcd.backlight();
  lcd.home();

  // set modes for all output/inputs
  pinMode(QUIET_SWITCH, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  pinMode(SPEAKER, OUTPUT);
  pinMode(POT_PIN_HOUR, INPUT);
  pinMode(POT_PIN_MIN, INPUT);

  // inital state of speaker
  digitalWrite(SPEAKER, LOW);
}

void loop()
{
  char data = '0'; // state
  int distance = sonar.ping_cm(); // ultrasonic ranger
  int deltaX = 5; // distnance between sonar and object
  int potVal_hour = analogRead(POT_PIN_HOUR); // knob for hour increment
  int potVal_min = analogRead(POT_PIN_MIN); // knob for min increment
  bool movement = false; // if object was moved -> true (deltaX > detection_zone)

  // map values from potentiometers to hour/min/sec scale - range = [14sec, 24 hours]
  reset_threshold = calculate_threshold(potVal_hour, potVal_min);
  
  // translate potVal to ms scale (1 - reset_threshold)
  lcd.clear();
  lcd.print("hour: ");
  lcd.print(reset_threshold / HOUR_MS);
  lcd.setCursor(0,1);
  lcd.print("min: ");
  lcd.print((reset_threshold / MIN_MS) % (unsigned long)60);
  lcd.setCursor(9,1);
  lcd.print("sec: ");
  lcd.print((reset_threshold / SEC_MS) % (unsigned long)60);

  run_time = millis();

  // Quiet mode detection
  if (digitalRead(QUIET_SWITCH) == HIGH) {
    // mode - true (LEDs)
    quietMode = 1;
  } else {
    // mode - false (Sound)
    quietMode = 0;
  }

  // Sonar distance detection (lack of motion detection)
  if (distance > deltaX) {
    sonar_zeros_in_row += 1; // smoothing fn
    if (sonar_zeros_in_row == 3) { // 3 in a row to avoid false positives
      // object was moved for sure
      movement = true;
      data = '1'; // change state
      sonar_zeros_in_row = 0; // reset this smoothing variable
    } else {
      // stay in the same state of '0'
      data = '0';
    }
  } else {
    // distance stays under 5
    movement = false;
    sonar_zeros_in_row = 0;
    data = '0'; // stay in the same state of '0'
  }

  // object moved - set state
  if (movement == true) {
    data = '2'; // state of object has moved
  }
  
  // object has been unmoved for too long
  if ((run_time - prev_time) >= reset_threshold) {
    prev_time = run_time; // reset timers
    data = '1'; // change state to '1' - sound the alarm
  }

  // reset led after 5 seconds
  if (led_on == true && (run_time - led_start_time) > 5000) {
    led_on = false;
    // turn off LEDs
    for (int i = 0; i < LED_COUNT; i++)
    {
      colors[i] = rgb_color(0, 0, 0);
    }
    ledStrip.write(colors, LED_COUNT);
  }

  // STATE '1'
  if (data == '1') {
    if (quietMode == 1) {
      // quiet alarm
      led_start_time = run_time;
      led_on = true;
      
      // upload rainbow color based on time to LEDs
      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);
      delay(10);
      
    } else {
      // play sound alarm
      for (int thisNote = 0; thisNote < 6; thisNote++) {
        // Loop through the notes in the array.
        TimerFreeTone(SPEAKER, melody[thisNote], duration[thisNote]);

        // Play thisNote for its coresponding duration.
        delay(50); // Short delay between notes.
      }
    }
  // STATE '2' - reset reminder
  } else if (data == '2') {
    prev_time = run_time;
  // STATE '0' - do nothing
  } else if (data == '0') {
    Serial.print("waiting");
  }
  Serial.println("");
}

/*    Usage: Function used to scale potentiometer values (2 of them - one for
 * hours and one for min) to a scale of [14sec, 24 hours] as evenly as 
 * possible
 * map potvals (0-1023)  
 */
unsigned long calculate_threshold(int potVal_hour, int potVal_min) {
  unsigned long one_percent_hour = SCALE_POT / 100;
  unsigned long one_percent_min = SCALE_POT / (100 * 60);
  unsigned long result_hour;
  unsigned long result_min;

  float hour_percent = ((float)potVal_hour / 1023.0) * 100;
  float min_percent = ((float)potVal_min / 409.0) * 100;

  result_hour = (unsigned long)hour_percent * one_percent_hour;
  result_min = (unsigned long)min_percent * one_percent_min;

  return result_hour + result_min;
}

 

]]>
Make Me Wake Alarm Clock https://courses.ideate.cmu.edu/60-223/s2019/work/make-me-wake-alarm-clock/ Wed, 20 Mar 2019 11:51:58 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6800 Overview

This project features an alarm clock that can only be disabled by pressing a button located across the room from the users sleeping location.

Device Overview

User interface consisting of an I2C LCD display, 2 potentiometers, and a push button.

User turning off alarm using the deactivation button.

Process Images and Review

As a mechanical engineer, I have developed a fascination with the working world around me. I like to see how things work and how they differ from other objects and products similar to them. This prompted me to construct  my final iteration using clear acrylic to house the electronic components of my alarm clock.

I originally intended to use an LED light strip mounted above the users bed as another method of notifying the user to wake up. I decided against this due to the extra power required and increase in the wiring needed to be installed in order to have the lights mounted externally.

Clear acrylic allows user to see the inner workings of the clock and also makes it easier to see the LED when alarm is triggered.

LED mounted inside the device decreases the amount of external wiring required and also illuminates and emphasizes the electrical components of it.

  • Clear acrylic allows user to see the inner workings of the clock and also makes it easier to see LED when alarm is triggered.

    Discussion

    I received a lot of valuable feedback during the in-class critique. One of the responses recommended the following: “It would be cool if a bluetooth module was added to connect the two parts instead of string.” This idea crossed my mind as well. I ultimately decided not to include a bluetooth. In an attempt to make the device more power efficient, I decided against using methods involving extra power and/or a constant need for power. This is also the reason I decided against using an LED strip and including a continuously running clock. Other feedback that I received stated “maybe add a different power source which would make it more long term.” I could not agree with this more. The end goal of the project was to include a power supply that could be plugged into a wall outlet. I chose to keep the battery as a power source to ensure easy setup and portability during  the building and revision process as well as the peer feedback portion of the project.

  • Overall I am satisfied with the result of my project. It does everything that I intended for it to do while remaining in line with my goal of reducing overall power needs for the gadget. Although I met my main goals for the project, I found that reducing power led to my project having less of a wow-factor to it.  Along with this sort of interesting vs. efficient balance that exists, this project also helped me come to notice some of the patterns that I have developed in terms of my own work flow. It took me some time to figure out how to use the I2C LCD screen that I implemented in my device. I spent countless hours trying to figure this out when I could have spent that time working on other aspects of my project. It has helped me realize the importance of utilizing the resources that are available to me as early and often as possible so that I do not waste as much time working on something that can be accomplished much faster with a little bit of help. This is definitely something I would do if I had the chance to do it all over again. I do not see myself iterating again on this project, but if I were to, I would most likely make the device invoke more of a sense of urgency. By this I mean more lights and more noise. After being placed in the enclosure, the noise became a little quieter than I had intended, and this is definitely something that can be easily fixed.
  • Technical Information

  • Wiring Schematic

 

  • Code 
  • /*Wake Me Wake Alarm
    Justin Belardi
    
    Funtionality: 
    The following code uses readings from 2 potentiometers to set values for hours and minutes. 
    Upon the press of the set button, the software sets a timer based on the values from the
    potentiometers. When the timer reaches zero, an LED flashes and a buzzer beeps, prompting 
    the user to wake up. The alarm continues sounding and flahing light until the user presses 
    the stop button located across the room.
    
    Pin Mapping:
    Hour Changer: Pin A0
    Minute Changer: Pin A1
    Set Time Button: Pin 2
    Diasrm Button: Pin 3
    LED: Pin 6
    Buzzer: Pin 9
    
    Credits:
    Intro to Physical Computing Code Byte - Displaying data on an I²C LCD screen 
    -Used for learning how to implement the I2C LCD Screen. 
    -Setup Code for initializing Screen was used in final software for project
    */
    
    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>
    
    const byte HRPOT = A0;
    const byte MINPOT = A1;
    const byte SETBUT = 2;
    const byte STOPBUT = 3;
    const byte LED = 6;
    const byte buzzer = 9;
    
    //Initilize variables used to set timer and countdown
    int hrRead = 0;
    int minRead = 0; 
    int hours = 0;
    int mins = 0;
    int timer = 0;
    int hrsLeft = 0;
    int minLeft = 0;
    int secLeft = 0;
    
    //Initializing state variables that are used to tell the software which 
      //loop that should be run based on previous code and user input.
    volatile byte setState = LOW;
    volatile byte stopAlarm = LOW; 
    
    LiquidCrystal_I2C screen(0x27, 16, 2);
    
    
    void setup(){
      pinMode(HRPOT, INPUT);
      pinMode(MINPOT, INPUT);
      pinMode(SETBUT, INPUT_PULLUP);
      attachInterrupt(digitalPinToInterrupt(SETBUT), start, RISING);
      pinMode(STOPBUT, INPUT_PULLUP);
      attachInterrupt(digitalPinToInterrupt(STOPBUT), alarmOff, RISING);
      pinMode(LED,OUTPUT);
      Serial.begin(9600);
      
      screen.init();
      screen.backlight();
      screen.clear();
    }
      
    void loop(){
      if (setState == LOW){ //If the set button has not been pressed yet...
        screen.home();
        screen.print(" Set Sleep Time ");
        stopAlarm = LOW;
        hrRead = analogRead(HRPOT);
        minRead = analogRead(MINPOT);
        hours = map(hrRead,0,1023,0,24);
        mins = map(minRead,0,1023,0,60);
        screen.setCursor(0, 1);
        screen.print((String) "Hrs: " + hours + "  Min: " + mins +"  ");  
        timer = hours*3600 + mins;
        Serial.println((String) "Hours: " + hours + " Minutes: " + mins);
      }
      else{
        //simulates a timer by looping until a value of zero is reahed
          //while reducing time value by 1 and delaying 1000 ms (1 second)
        while (timer > 0){ 
          timer = timer - 1;
          Serial.println(timer);
          delay(1000);
          screen.home();
          screen.print("  Sweet Dreams  ");
          screen.setCursor(0, 1);
          screen.print("...zzzzzzzzzz...");  
           
        }
        while (stopAlarm == LOW)
        //after the timer reaches zero, this block of code runs until
          //the user interrupts the signal by pressing the stop button
        {
          screen.home();
          screen.print("TIME TO WAKE UP!");
          screen.setCursor(0, 1);
          screen.print("Press Big Button"); 
          digitalWrite(LED,HIGH);
          tone(buzzer,1000);
          delay(500);
          digitalWrite(LED,LOW);
          noTone(buzzer);
          delay(500);
          setState = LOW;
        }
      }
    }
    
    
    //following 2 loops change the value of the state values on the
      //activiation of the interrupt buttons
    void start(){
      setState =! setState;
    }
    
    void alarmOff(){
      stopAlarm =! stopAlarm;
    }

     

]]>
Muppet Box https://courses.ideate.cmu.edu/60-223/s2019/work/muppet-box/ Wed, 20 Mar 2019 08:00:06 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6295 The Muppet Box is a fidget device that incorporates a mini-game for those who aren’t simply content flicking a switch back and forth, using limit switches, LEDs, and vibrating disc motors.

The front view of the Muppet Box. Note that the wires and electronics are hidden.

The rear view of the Muppet Box.

A close up of the power button. Hypothetically, the only visible button interface of the Muppet Box.

 

The Muppet Box in action.

Process

Starting the project, I was much more concerned about the size and final shape of the cube, but decided to start with electronics given my available materials.

A breadboard-based prototype to test code only.

I made an Arduino Uno and breadboard prototype to set up the wiring and then moved onto soldering the pieces onto these small sawed-off proto boards. Everything went pretty swimmingly save for minor debugging and dumb wiring mistakes, as I was pretty new to the limit switches I used as well as soldering.

A set of 3 proto-board segments hooked up to the Arduino for testing.

One design issue that I had to take a moment to ponder is how I wanted the cube to incorporate the 5 proto-board segments. I was very set on the cube shape, but I also already had the idea of something soft and squishy in mind. I ended up starting off by soldering three of these segments together to make a corner, but not only was it precarious of a solder-job, but it also was not conducive to future debugging given the proximity of the pieces. There were a few regrettable decisions with the ground wires touching, for one.  I decided to put the assembly of the cube off until I decided what to do about the furry cover. Mid-way, I switched from the Uno to the Nano for size purposes, as per my original plans.

Sewing the blue fabric together.

A fortunate coincidence saddled me with this very soft blue fabric from Creative Reuse, so I decided to focus on how soft the project was gonna be. I started by cutting 6 squares of fabric out, then attaching two squares to each other. In the middle of sewing, it occurred to me that it would be much more exponentially difficult to sew in the electronics after finishing the fur cube, so I made a few rash decisions to sew each of the proto-board segments into the center of their respective square, then hope for the best. (Specifically, I threaded some string through the niche in the limit switch lever, fixed it to the fabric, then the LEDs, then tied it off.) The sewing process was doable but precarious. In the end it turned out weird and lumpy but acceptable. I nixed the idea of six proto-boards in the middle of this process. It was much easier to have one panel be a debug/power button/base side.

The final look of the Muppet Box, fresh from my hands.

However, the sixth panel being loose and about meant some quick thoughts regarding how to fix it into place. In an ideal world I would have used zippers, but I had no zippers at the time. I got away with some similarly-colored pipe cleaners sewn to the mouth of the box to both hold its shape and to be able to tie them together (with more pipe cleaner). For demonstration purposes I would simply tuck the ends into the box, which was overstuffed with wires anyway.

At this point, a lot of debugging went into the badly soldered insides of the box, and I had to battle fluff and poor lighting conditions to re-do some ground wires that had broken apart. Regrettable, but necessary and character-building. It really taught me that I should solder more tightly.

Discussion

Response to Critique

The vibrating aspect of it is creative and the lights are fun. I like how the lights are the same color as the fidget cube so you have to actually pay attention a little bit.”

I love the textile aspect to this! It’s so soft and calming, I love the vibrations and lights!”

The design and form factor is super cool, looking at this I wouldn’t even think it’s an Arduino project. The vibration and the fuzzy fabric feel really nice.”

Really ergonomic and fun looking fidget cube that will probably keep stressed people / hyperactive kids at bay  for a while”

One of the main factors of a fidget cube is tactile pleasure, which I tried to emphasize in the making of this project. I am certainly glad to hear that my attempts worked, and that it was successful as a toy.

“Really cool game. It’s really nice to feel and fun to find where the lights are. One aspect that may be interesting to pursue is to make it full integrated within the fur box. That way it becomes fully portable and doesn’t rely on a working external power source to function.”

Really really cool. It’s nice and compact, very impressive that you were able to fit all your electronics in there. Maybe finding a way to power it externally, instead of through your computer. “

One way you can possibly improve is by using a barrel jack and battery, that way it doesn’t have to be tethered to a computer to work. Other than that, really interesting and extremely unique project.”

This was a point of critique that really struck a chord with me. This device was always meant to be extremely portable and non-obstructive, so having it detached from an external power source was a very good idea. I implemented it immediately after hearing it, settling on coin batteries.

Self-Critique

To summarize my views on the project, I would say that it was an odd process. Starting with electronics was a bit of a mistake, because I had to design around what I had made without testing how it felt. This resulted in a weirdly lumpy spots and less than ideal wiring situations. In addition, the vibrating motors, while very pleasant to the touch and not a regrettable choice, did not work towards gameplay. I wish I had focused much more on playtesting what the game had to offer, which would have taken this project above and beyond. However, I am also cognizant of my (lack of) abilities in the hardware department, and can forgive myself for having spent so much time on re-soldering things over and over.

What I Learned

Some rather random things: I should do my planning more in advance, as so to deal with unexpected curveballs. Fluff sheds really badly, so do your soldering super well beforehand in order to not feel anxious over burnt fur. Make more prototypes to test how stuff would work before throwing all your chips in.

Next Time

I hope to incorporate the mistakes in planning. If I am to create a very tactile object, I should start with the tactile plans and work from there to the electronics. In addition, I would like to plan my soldering better and be more careful with exposed wires to cut down time in that part of the process. I would like to incorporate fabric earlier, as the combining of electronics and fabrics was ungainly, to say the least. The vibrating motors may need tweaking, as they weren’t as precise as I wanted them to be. The switches, while nicely tactile, are too easy to press. I should test other buttons under fabric. The fur is obscenely blue, and while it might be okay, one might prefer calmer colors for a calming item.

Technical Information

Schematic

/*
 * Muppet Box
 * Sophia Cao (sccao)
 * 
 * This code runs a short game based on a modifiable 
 * number of limit switches and equal number of LED and 
 * vibrating disc motor outputs. 
 * 
 */
 
// initializing the pins for the hardware
const int LVRPINS[] = {2,3,4,5,10}; 
const int LVRNUM = (sizeof(LVRPINS))/(sizeof(const int));
int PREOUT[LVRNUM];
int LVROUT[LVRNUM];

const int LEDS[] = {8,7,6,9,11}; 
int LEDOUT[LVRNUM];

const int VIBS[] = {A1,A2,A3,A0,A4  }; 
int VIBTENSITY = 128;

//necessary variables
int curr;
const int debounce= 100;
int timer;
bool paused;
int pause_amt = 500;

//initialize the pins
void setup() {
  for (int i=0;i<LVRNUM;i++){
    pinMode(LVRPINS[i],INPUT);
    pinMode(LEDS[i],OUTPUT);
    pinMode(VIBS[i],OUTPUT);
  }
  Serial.begin(9600);
}

void loop() {
  timer++;

  //pause state
  if (paused==true) {
    set_items(-1);
    if (timer>pause_amt){
      paused=false;
    }
    return;
  } else {
    
    set_items(curr);
  }
  
  //check buttons for press. serial input for if the correct 
  //button was pressed.
  for (int i=0;i<LVRNUM;i++){
    PREOUT[i] = LVROUT[i];
    LVROUT[i] = digitalRead(LVRPINS[i]);
    if ((PREOUT[i]!=LVROUT[i]) && !LVROUT[i]){
      if (i==curr) {
        init_curr();
        Serial.println("winner winner chicken dinner!");
        timer=0;
        paused = true;
      } else {
        init_curr();
        Serial.println("you lose!");
      }
    }
  }
  delay(debounce);
  
}

//picks a new unit to set off
void init_curr(){
  curr = (int) random(0,LVRNUM);
}

// activates the pin (c) that needs to be activated.
// else, deactivates all other pins.
void set_items(int c) {
  for (int i=0;i<LVRNUM;i++){
      if (c==i){
        digitalWrite(LEDS[i],HIGH);
        analogWrite(VIBS[i],VIBTENSITY);
      } else {
        digitalWrite(LEDS[i],LOW);
        analogWrite(VIBS[i],0);
      }
  }
}

 

]]>
Unlock the Bathroom Door https://courses.ideate.cmu.edu/60-223/s2019/work/unlock-the-bathroom-door/ Wed, 20 Mar 2019 07:45:56 +0000 https://courses.ideate.cmu.edu/60-223/s2019/work/?p=6760 My project is meant to unlock the bathroom door after no motion for 30 minutes to prevent against us getting locked out when our suitemates forget to unlock our side.

This is how my project is setup on the door with a proper view of its housing.

pir motion detector in cutout made for it

Motor mounted on top with slot for lever arm

internal wiring

Decision Points:

  1. I was having a really hard time trying to get my LED strip to either flash red or flow through the strip with a gradient in the context of my code. I was able to make it work separately, but once put into this code, it would only stay on red. I was in the lounge of my dorm when I was playing around with the gradient example code from the Pololu library while trying to understand why it worked in one context but not the other; I was extremely frustrated and my suitemate who was also in the lounge commented that that while the flashing and gradient looked really cool, it was a bit dizzying. I then asked her what she thought about the lights when it was just on by itself and she said it was more than enough to get her attention and it was why she had started to look at what I was doing in the first place. Because of this, I settled on having the LED strip just light up red instead of spending more time trying to make it have a pattern.
  2. Another decision that I had to make was the acrylic for the housing. I was choosing between making it look cleaner with white or what I personally believed was cooler and would stand out more with green tinted or more of an in-between with frosted. I really liked the green tinted acrylic after I saw it on someone else’s project in the Fab Lab a few weeks prior, but I knew it would mean that all of my wiring would be completely visible. White acrylic on the other hand would make my project look much more aesthetically professional. Frosted acrylic would mostly hide the wiring and would have an ominous aspect to it. After talking to my roommate and suitemates who I share the bathroom with, I learned that they also thought seeing the wiring was cool. With a few extra opinions that supported my first choice, I went with that even if to others it would seem aesthetically worse as this project was to be for us.

Process:

initial idea

1st sketch with door in mind-would be hung on hangers

sketch with shelf but LED strip starting on opposite side

testing in setting

soldering leads on LED strip

measuring dimensions on door

measuring electronics for box dimensions

figuring out box dimensions

solidworks of box and motor platform

Discussion:

Overall, I am pretty happy with the way my project turned out. While it is by no means flawless and there are some things that I would do differently, I learned a lot from this experience and most important, I know that I did the best that I possibly could have and put in more than enough time and effort. While the motor was unable to actually unlock the door, I am very happy that I was able to make something that did exactly as I wanted it to in every other aspect. As someone reiterated, I “thought about a lot of different use cases for [my] device,” and I’m glad to say that the contingencies do function properly. Not being able to unlock the door is a mechanical problem separate from my code and electronics that with the materials available that are Arduino compatible, is out of my control. If I were to try and make this work, I would use the HS-5645MG motor by HiTEC. That motor unlike the one I used is made to have a much higher torque, but it isn’t programmed by the Arduino and so I used the most powerful motor that was.

I think that my project looks pretty cool with the tinted green acrylic and being able to see all of the wires; however, I do agree with someone who said my project “could be more compact so as to minimize space.” This would make it take up less room on the door as well as minimize the chances of someone brushing into it when entering and leaving the bathroom. I could have done this by soldering on a board. The sound detector is being rented from lending, but I could have soldered most other components with smaller wires so that it would take up much less space. This would have been a real challenge I learned that soldering is something that I need more practice at. Many of my connections had to get re-soldered on because I didn’t do a good enough job the first time and the few wires that I did solder took me a long time. Someone else had commented that “less LEDs could be used as more of a meter display of how long movement hasn’t been detected.”  While I do think I went a bit extreme on the number of LEDs used, I still like having the LEDs go on only as a warning. I believe that the meter would be an annoyance to whomever is in the bathroom.

Many of the lessons that I learned from this project can be summed up as someone else said, that in physical computing, a project tends to not work when you need it to. I learned just how frustrating it can be to try and figure out a hardware electronics problem as there are so many things that can go wrong, and it tends to be something that you don’t necessarily expect. As I was trying to get a picture of my project working for this documentation, my battery pack wasn’t supplying power even though the batteries weren’t dead. At first I realized that the ground wire was frayed, but that wasn’t the root problem. It took a very long time to figure out and fix, but it turns out that in the holder, the batteries weren’t properly touching the ends of the case as a spring was stuck at a too compressed state. I also learned the importance of covering up leads, especially on power and ground. As I was just about done with my project and only had to hot glue the motor on, I accidentally touched power to ground while I was unplugging it from the breadboard to try and save the batteries for the crit. This caused quite a bit of damage and took awhile to fix. Seemingly small problems made this project quite frustrating electrically, but thankfully, the mechanical hardware part of it went well for me; my knowledge that I’m better in that realm was echoed from this project. Coding wise, I learned quite a bit and it went much more smoothly than electrically. I learned about interrupts as well as how to reset to the beginning of a loop with one. I also learned the usefulness of the time library and how to use a pir motion detector.

Schematic

Code:

/*
 Unlock the Bathroom Door
 By Seema Kamath

 Description:
 This code checks the bathroom for motion and will unlock it after 30 minutes without movement with two safeguards. The first of these is there is a while loop 
 that only runs when the noise in the bathroom is less than when the shower is on. This prevents the door from accidently unlocked if someone is in the shower for 
 longer than 30 minutes. The second safeguard is if somone is in the bathroom, but very still for a long period of time, they are given a warning to move by the LED 
 strip turning red. As long as they move while the LED strip is on, the motor will not turn on to unlock the door.

 Pin Mapping:
 The gate on the sound detector is connected to pin 2.
 The envelope on the sound detector is connected to pin A0.
 The Digital in wire on the LED strip is connected to pin 7.
 The servo motor is conncted to pin 12.
 The PIR motion detector is conntected to pin 3.

 Credit:
 I got TimeLib.h from https://github.com/PaulStoffregen/Time as well as learned about millis() from here.
 I learned about the pir motion detector from https://dronebotworkshop.com/using-pir-sensors-with-arduino-raspberry-pi/
 I learned about resarting loops with interrupts from http://forum.arduino.cc/index.php?topic=45686.0
*/

bool reset;
int showerLevel = 600; //soundVal when shower on
#include <TimeLib.h>
const int PIN_GATE_IN=2; //connected to gate on sound detector
const int PIN_ANALOG_IN=A0; //connected to envelope on sound detector

#include <Servo.h>
Servo myLittleMotor;
const int SERVOPIN = 12;

#include <PololuLedStrip.h>
PololuLedStrip<7> ledStrip;//LED in pin 7
const int LED_COUNT=120;
rgb_color red[LED_COUNT];
rgb_color black[LED_COUNT];

const int PIRPIN = 3;
void pirISR() {
  int pirVal = digitalRead(PIRPIN);
  reset = true;
}

const int LEDPIN = 7;
void setup() {
  Serial.begin(9600);
  pinMode(PIN_GATE_IN, INPUT);
  pinMode(PIRPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PIRPIN), pirISR, HIGH);//this calls void pirISR() whenever there is motion and makes the PIRPIn an interupt
  myLittleMotor.attach(SERVOPIN);
  delay(60000);//1 minute delay is needed so that the pir motion sensor can get accostumed to its current environment
}

void loop() {
  int pirValue;
  int soundVal;
  unsigned long currentTime; //using unsigned long so that this will be able to keep track of the time passed for more than just 30 seconds as I'd need it 
  //for much longer
  int timePassed;
  soundVal = analogRead(PIN_ANALOG_IN);
  Serial.println(soundVal);
  Serial.println("resarted");
  while (soundVal < showerLevel) {
    pirValue = digitalRead(PIRPIN);
    Serial.println(soundVal);
    static unsigned long startTime = millis();//only happens at begginning because of static
    currentTime = millis();
    timePassed = abs(currentTime - startTime);
    Serial.println(timePassed);
    if (reset) {
        Serial.println("reset");
        reset = false;
        break;}
    if (pirValue == 1) { //motion detected
      Serial.println("motion detected");
      delay(6000);//takes 6 seconds for motion sensor to reset
      startTime = millis();
    }
    else if (timePassed >= 1800000) {//30 minutes of no motion
      unsigned long LEDstartTime = millis();
      unsigned long LEDtimeNow = millis();
      byte time = millis() >> 2;
      while (abs(LEDtimeNow - LEDstartTime) < 5000) {
        LEDtimeNow = millis();
        Serial.println(abs(LEDtimeNow - LEDstartTime));
        for (uint16_t i = 0; i < LED_COUNT; i++) {//this rotates through each LED on the strip so that each one will be red
          byte x = time - 8 * i; 
          red[i] = rgb_color(255 - x, 0, 0);
        }
        ledStrip.write(red, LED_COUNT);
        delay(10);
      }
      for (uint16_t i = 0; i < LED_COUNT; i++) {
        black[i] = rgb_color(0, 0, 0);
      }
      ledStrip.write(black, LED_COUNT);
      if (reset) {
        Serial.println("reset");
        reset = false;
        break;
      }
      delay(500);//gives a little bit of time before the motor starts so that the LED strip and motor are in no way competing fot the battery's power
      Serial.println("move");
      myLittleMotor.write(150);
      delay(2000);//gives enough time for motor to have unlocked door
      myLittleMotor.write(180);//returns motor to horizontal position
      startTime = millis();
    }
  }
}

 

]]>