Project 2 – Intro to Physical Computing: Student Work Fall 2021 https://courses.ideate.cmu.edu/60-223/f2021/work Intro to Physical Computing: Student Work Tue, 02 Nov 2021 14:59:53 +0000 en-US hourly 1 https://wordpress.org/?v=5.8.9 Electric Tape Measure https://courses.ideate.cmu.edu/60-223/f2021/work/electric-tape-measure/ Tue, 02 Nov 2021 13:47:34 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14460 For this project I made a device that will record the measurements you take on an lcd screen using a string wound around a rotary encoder.

Overview:

Top view on welcome screen

View of the bottom with laser engraving of which measurements to take. The glue I used to keep the Arduino in place took its toll on the acrylic

You can see the nut which is attached to the string used for measuring

From this angle you can see all the circuits and the spool on different layers

testing the device against a ruler, though the device is a little off here

This is a video of me testing the device against a ruler, after some calibrating the device works with results within a range of 5mm from the expected result. Featuring a small guest.

Process Images:

a screenshot of the measurements for the spool I 3D printed

At first I was a little more ambitious about the design and wanted to use a spring to rewind the string, but that turned out to be too much of a mechanical puzzle for me. My next thought was to use a crank, and I actually included holes in the spool for one to attach, but after the design was printed, cut, and put together, it became clear that the best method was the easiest one, just using my finger to rewind the string.

freshly laser cut, seeing what it looks like put together and figuring out which spacers work best

After I put my project together I realized that I needed to plan out the design better so that everything would fit. I went back afterwards and got rid of the excess wires and used glue instead of electrical tape to keep it all together, I was super happy with the 2nd version as compared to the bulkiness of the first (which had the buttons outside of the case). I was also able to switch out the yarn I was using with a more sturdy string and that made the results way more consistent, which made the project a lot more enjoyable.

First version of the project put together, I was having trouble using the wires with pins and couldn’t fit everything inside. I was also using yarn instead of the sturdy thread now being used.

soldering a tiny protoboard so I can get rid of the breadboard and get everything to fit

Shirt made in Illustrator for engraving

 

Discussion:

“With more time try having it be battery powered to be completely portable ”

I totally agree. I’m not sure in the current design where I would place a battery, but I’m sure there’s room for something. I think the wires had a little too much space to spread out, in a future iteration I would like to set up a path for the wires rather than the open design so that I can better place other parts of the project.

“A closed design might be better for something like this”

I think I agree, especially with the wires adding to the bulk. In the first version, pre soldering, the wires were sticking out the sides and it was pretty messy, though I think after cleaning it up the open design isn’t so bad to look at. I think the project looks cooler having the layers open like they are, but I think it also makes the project a lot more fragile.

“Millimeters is probably not the most useful unit for measurement?”

I agree. Since mm are such a small unit though, for the purpose of testing I thought they would be better to use. If I had more time and also more room on the LCD it would be useful to convert the measurement into inches or cm.

I really liked my final product. At first I was disappointed by the bulkiness of it, and the difficulty in getting an accurate reading, but after some soldering and a change of string, I was happy with the results. There are a lot of mistakes I made, and I feel that I could have gone farther with the case, but for a first laser cutting experience I thought it turned out well.

I feel that I learned a lot through this project. I struggled just to get the rotary encoder working, and if it wasn’t for Zach explaining the math, I’m sure it would have been a nightmare for me to figure out how to turn those measurements into distance. Though now looking back, I’m surprised that I struggled so much. I feel more prepared than I was before in starting what I’d consider a ‘technical’ project. I’m a big fan of motors and lights, but translating an input to an accurate reading is something I would usually avoid. After seeing the project work though, I feel a lot more confident. Something I would do differently next time is prepare the structure a little more. I like the design I used, but while putting it together I realized how much easier it would have been if I had taken every wire into consideration, not just the parts they attach to. I also ended up using glue to put the pieces in place, but in hindsight I would have preferred to use screws as much as possible.

I also learned how to laser cut (at least with the help of the IDeATe staff) and I want to try it out again soon!

If I were to iterate on this project I would definitely change the screen to an OLED, theres just not enough room on the LCD to show the readings. Also I’d like to add a battery so that the device is portable. One thing I wasn’t able to get to that I originally thought would be cool is to use a spring. This way the string would automatically rewind itself after being pulled out. I opted instead for a crank, but it became clear it was faster just to wind the spool itself. I also would rework the case and add holes for the Arduino and LCD to attach with screws. I might also add markings to the string just for clarities sake, and maybe figure out a way to make the engraving more visible, or less obstructed. I could add a receipt printer using bluetooth that could print out the measurements taken. Lastly I’d like to add a button that could convert the units into different measurements.

 

Technical Information:

Schematic and Block Diagram:

 

Code:

/* 
Electronic Tape Measurer
John Hewitt
This project uses a rotary encoder to take clothing measurements in mm

The buttons map to pins 6 and 8, while the rotary encoder is mapped to pins A2 and A3

I used this setup to get the rotary encoder working
https://forum.arduino.cc/t/getting-input-from-rotary-encoders/620026

*/

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

 
LiquidCrystal_I2C screen(0x27, 16, 2);


 // used to draw a lil heart :] https://maxpromer.github.io/LCD-Character-Creator/ 
byte heart[] = {
  B00000,
  B01010,
  B10101,
  B10001,
  B01010,
  B00100,
  B00000,
  B00000
};

int clockP = A3;
int data = A2;
int count = 0;
int c = HIGH;
int cLast = HIGH;
int d = HIGH;


int collar = 0;
int sleeve = 0;
int chest = 0;
int waist = 0;

float current = 0;

int enter_button = 6;
int zero_button = 8;

float measurement;
float measurement_last;

bool no_change_on_screen = true;

bool measurement_mode = false;

bool start_screen = true;
bool start_display_up = false;

bool still_measuring = false;

int on_step = 1;

void setup() {
  
  pinMode (enter_button, INPUT);
  pinMode (zero_button, INPUT);

  pinMode (clockP, INPUT_PULLUP);
  pinMode (data, INPUT_PULLUP);
  
  
  screen.init();
  screen.backlight();
  screen.home();
  screen.display();
  screen.createChar(0, heart); //initialiaze heart to zero screen.write(0);
  screen.clear();
}




void loop() {


  //rotary encoder code 
  c = digitalRead(clockP); // read pin A as clock
  d = digitalRead(data);  // read pin B as data
  
  
  if (c != cLast) {       // clock pin has changed value
    d = c^d;              // work out direction using an XOR
    if ( d ) {
      count++;
    } else {
      count--;
    }
    cLast = c;
  }


  if (digitalRead(zero_button) == HIGH) {
    //set current value to zero
    count = 0;
  }

//43.9823
  measurement = ((((float)count/ (float) 16)) *30);
  


  if (start_screen == true) {
    if (!start_display_up) {
      screen.setCursor(2, 0);
      screen.write(0);
      screen.print(" WELCOME ");
      screen.write(0);
      start_display_up = true;
    }
    if (digitalRead(enter_button) == HIGH) {
      start_screen = false;
      start_display_up = false;
      measurement_mode = true;
      no_change_on_screen = false;
      still_measuring = true;
      on_step = 1;
      delay(200);
    }
  }


  
  if (measurement_mode == true) {

    if (measurement_last != measurement) {
      measurement_last = measurement;
      no_change_on_screen = false;
    }

    if (still_measuring == true) {
      
      if (digitalRead(enter_button) == HIGH) {
        
        if (on_step == 1) {
          collar = measurement;
          on_step = 2;
          
          no_change_on_screen = false;
          
        } else if (on_step == 2) {
          sleeve = measurement;
          on_step = 3;
          
          no_change_on_screen = false;
          
        } else if (on_step == 3) {
          chest = measurement;
          on_step = 4;
          
          no_change_on_screen = false;
          
        } else if (on_step == 4) {
          waist = measurement;
          still_measuring = false;

          no_change_on_screen = false;
          
          still_measuring = false;
        }
        delay(200);
      }
    }

    if (!no_change_on_screen) {
      screen.clear();
    
      screen.setCursor(0, 0);
      if (on_step == 1) {
         screen.print("collar:");
         screen.print(measurement);
      } else if (on_step == 2) {
         screen.print("sleeve:");
         screen.print(measurement);
      } else if (on_step == 3) {
         screen.print("chest:");
         screen.print(measurement);
      } else if (on_step == 4) {
         screen.print("waist:");
         screen.print(measurement);
      }

      
      screen.setCursor(11, 0);
      screen.print("a)");
      screen.print(collar);

      screen.setCursor(0, 1);
      
      screen.print("b)");
      screen.print(sleeve);
      screen.print(" ");
  
      
      screen.print("c)");
      screen.print(chest);
      screen.print(" ");

      
      screen.print("d)");
      screen.print(waist);

      no_change_on_screen = true;
    }

    if (!still_measuring) {
      if (digitalRead(enter_button) == HIGH) {
        screen.clear();
        measurement_mode = false;
        collar = 0;
        sleeve = 0;
        chest = 0;
        waist = 0;
        start_screen = true;
        on_step = 1;
        delay(200);
      }
    }
    
  }
  
}

 

]]>
Out the Door Cabinet https://courses.ideate.cmu.edu/60-223/f2021/work/out-the-door-reminder/ Tue, 02 Nov 2021 13:01:14 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14418 Description

This “Out the Door Cabinet” is a cabinet that reminds me to take all the items inside the cabinet with me every time I leave my apartment, and also reminds me to put these items back to the cabinet when I enter. When I am in a hurry, I often times leave an important item, such as my keys, mask, umbrella, and etc., when I leave my apartment room, and when I come back home I tend to leave these items all over the place at random places. For my self-assisting device, I wanted to make a device that would help me fix these bad habits and remind me to take and put back these items from the cabinet when I leave and enter the door.

Images

Out the Door Cabinet

Inside of the cabinet: a removable middle shelf

Close-up of the lever switch and magnet that keeps the door closed when it is shut.

An ultrasonic ranger, a lever switch, a magnet that keeps the door closed, a removable middle shelf, and door hinges that allows the door to open and close.

Cabinet installed at home near the front door

Videos

Process Images

Cabinet design in Fusion 360

Testing LED Strip lights.

Hand-cut wooden case for the ultrasonic ranger. Cut a hole in the bottom of the cabinet to hide all the wires inside the back of the cabinet.

The arduino, breadboard, and all the wires are hidden inside the back of the cabinet, and there is another shelf that hides all these parts so that they are not visible.

Review

The way I worked on this project was, I worked on the wiring and computing part of the project and the fabrication part of the project separately. Therefore, after I was nearly completed with both parts I ran into the problem of how I put these together aesthetically, without ruining the design. Some of the solutions I came up with is first, I attached door hinges to the cabinet door so that I can easily open and close the cabinet. I also realized when I close the door, the door did not want to stay shut, so I decided to add a magnet so that the door will stay closed. Another problem was that I did not know where I should put the ultrasonic ranger, so I made a quickly made a case and attached it to the bottom of the cabinet, so that it can easily detect motion when I go near the cabinet. To hide all the wires I created a shelf to cover the back of the inside cabinet so that they are not visible.

Discussion

At first, I was not so enthusiastic about this idea of making a key reminder self assisting device. My concern was that the project would turn out to be either too simple and easy, or too complicated. I was sure that I wanted to use the ultrasonic ranger to detect motion when I am near my front door, and when it does, the LED strip lights would turn on. But this is a simple input and output therefore I wanted to add an extra component that would advance my idea. I thought about playing with the ultrasonic ranger so that it can tell whether I am leaving or entering the apartment, however I thought that it would be too complicated and not really necessary. I struggled to find the middle between too simple and too complicated, however during my prototype critique, some of the feedbacks I received from my classmates were really helpful. After hours of brainstorming, the solution that I came up with was to add a lever switch that detects whether the door is open or closed, and when the switch is closed, the blinking LED lights would turn off. Another problem I ran into was the wiring of the lever switch. I made a stupid mistake and didn’t remember to add a pull-down resistor. Because of this mistake, the switch was not functioning properly. Fabrication wise, I struggled to cut out a case for the ultrasonic ranger last minute since I did not have access to the laser cutter, however I went to the woodshop to cut it out by hand. It took me a while to figure out how to hide all the wires so that it does not look messy. I decided that I can use the back half part of my cabinet and hide it inside of the cabinet so that they are not visible. I cut a hole on the bottom of the cabinet so that the wires of the LED strip lights and the ultrasonic ranger are still connected, and I hand cut a shelf to hide all these components inside the cabinet.

During the final critique, I received some great feedbacks from my fellow students. First, I really appreciated their comments on the fabrication part of my project. It is definitely something I spent a lot of time on so I felt very relieved that people seemed to like it. Another comment that I really appreciated was, “Nice use of switch for detecting open/close state of the door”. It was really great to hear that people liked this component of my cabinet because it was a solution I came up with to a problem that I ran into in the beginning state of my project ideation. Another comment I would like to address is “Ultrasonic ranger seems finicky”. I would have to agree that the ultrasonic ranger was not working properly during the time of the critique. There was a small accident right before the critique and there were some wires that got disconnected that caused parts of my project to not work properly. However, they were all quickly fixed. 

Overall, I am satisfied with how my project turned out. This project was definitely a challenge for me, however I learned a lot from this experience. What I found the most enjoyable was the fabrication and design portion of this project. Fusion 360 is definitely something I would use again in the future so learning this program was very useful and valuable, and the same applies for learning how to use the laser cutter. As someone without any coding experience and background in engineering, I would say the most difficult part was the coding portion of the project. I tried to teach myself by searching up examples and watching several video tutorials and I would say it was a difficult and a frustrating process for me. However, through this experience, I definitely learned a lot.

I definitely am open to build another iteration of this project. This device is something that I needed and is something that will help me not only as a reminder, but as a product to help me fix my bad habits, such as leaving things at random places in the house. Some things I would do different is maybe add a speaker system that will remind me upcoming important events that will happen throughout the day. I think that would be a cool device, working as both a reminder to take/return items from cabinet and an agenda for the day.

Block Diagram & Schematics

Code

//Out the Door Reminder
//A cabinet that reminds me to take the itmes inside the cabinet when I leave the door, and put back the items into the cabinet when I enter the door.
//The Ultrasonic Ranger detects motion when I am near the door and when it detects motion, the LED strip lights up.
//When the cabinet door is open, the lights turn off.
//Credit: LED Strip code borrowed from PololuLedStrip library example: LedStripGradient.
//Credit: Some code borrowed from class website and arduino project hub
//https://courses.ideate.cmu.edu/60-223/f2021/tutorials/button-and-switch
//https://create.arduino.cc/projecthub/abdularbi17/ultrasonic-sensor-hc-sr04-with-arduino-tutorial-327ff6


#include <PololuLedStrip.h>

// ULTRASONIC RANGER
const int TRIG_PIN = 11; // Arduino pin connected to Ultrasonic Sensor's TRIG pin
const int ECHO_PIN = 10; // Arduino pin connected to Ultrasonic Sensor's ECHO pin
const int DISTANCE_THRESHOLD = 50 ; // centimeters

long duration;
int distance;


//LEVER MICROSWITCH
const int lswitch = 2;


//LED STRIP
PololuLedStrip<12> ledStrip;

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


void setup()
{
  Serial.begin (9600);       // initialize serial port

  //Ultrasongic Ranger
  pinMode(TRIG_PIN, OUTPUT); // set arduino pin to output mode
  pinMode(ECHO_PIN, INPUT);  // set arduino pin to input mode

  //Lever Micro Switch
  pinMode(lswitch, INPUT);  //set arduino pin to input mode

}

void loop()
{
  //generate pulse to TRIG pin
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(5);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);


  //measure duration of pulse from ECHO pin
  duration = pulseIn(ECHO_PIN, HIGH);
  // calculate the distance
  distance = (duration / 2) * 0.0344;



  //if the ultrasonic ranger detects motion/distance, then the LED strips light up

  if (distance < DISTANCE_THRESHOLD)

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

    delay(10);
  }

  // if the ultrasonic ranger does not detect any motion/distance, the the LED strips do not light up

  else {
    for (uint16_t i = 0; i < LED_COUNT; i++)
    {
      colors[i] = rgb_color(0, 0, 0);
    }
  }


  //if door is open, it turns off the LED light that was turned on due to the detection of motion/distance
  if ( digitalRead(lswitch) == HIGH )
  {
    for (uint16_t i = 0; i < LED_COUNT; i++)
    {
      colors[i] = rgb_color(0, 0, 0);
    }
  }



  //  //if the door is closed it prints "door is closed"
  //    if ( digitalRead(lswitch) == LOW)
  //    {
  //      Serial.println("door is closed");
  //      //      delay(20);
  //    }
  //
  //    //if the door is open it prints "door is opened"
  //    if ( digitalRead(lswitch) == HIGH )
  //    {
  //      Serial.println("door is opened");
  //      //      delay(20);
  //    }


  //  //     print the value to Serial Monitor
  //  Serial.print("distance: ");
  //  Serial.print(distance);
  //  Serial.println(" cm");
  //  delay(250);


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

 

]]>
Accessory Keyboard https://courses.ideate.cmu.edu/60-223/f2021/work/accessory-keyboard/ Tue, 02 Nov 2021 13:00:03 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14438 An handy set of programmable keys to keep your most used functions within reach.

Photos:

Standard use, first four keys 1-4, 5th key mapped to long message

A cinematic view with plants!

Next to a tape measure for scale

A top down view of the layout

A use case example. On a desktop as shortcut keys.

Process Photos:

early component testing, including a mess of wires, resistors, and eraser shavings

Initial ideation, input and format brainstorming

struggling to portion out inputs and understanding requirements

Decision Points:

A clear view of the wiring matrix and efficiently used potentiometer power and ground

Wiring Strategy

A critical consideration when designing a custom keyboard is wiring. This was an unexpected step which affected far more than I thought it could. The Arduino pro micro is an exceptional piece of technology, but is only built to handle so many inputs. This has led enthusiasts the apply creative workarounds and allowed them to achieve exceptional results. The critical option for me was individually wiring each switch or forming them in an array and addressing the problem of ghosting with diodes. Individual wiring would be far more messy and setting up buttons in a matrix was generally the practice in most custom boards. I decided to hand wire a neat matrix and follow the steps someone might use for a much larger keyboard to further immerse myself in the learning process. With careful wire stripping and many soldering attempts, I was able to neatly lay out the matrix for eight switch inputs.

notoriously buggy flashing software

guide for wiring, showing example of for a ‘plank’ layout

 

HID vs Arduino

When sending signals from the Arduino to the computer there are two ways the computer can read them; as input from an Arduino device that needs to be translated, or as native keyboard inputs from an HID. Following the spirit of a fully native keyboard, I attempted to flash my Arduino pro micro using community developed tools. The process of flashing skips over the step where a computer has to read a device as an Arduino before receiving simulated keyboard inputs and would hopefully lead to a more robust tool. This option included several external programs to feed matrix diagrams into very abstract code. Ultimately, despite investing my efforts into this step in order to make a native keyboard device very specific issues, such as a missing .dll file that affected how my computer’s USB port communication left me with software issues I could not understand well enough to fix. I was forced to keep the traditional Arduino input

Discussion:

The construction of a macro keyboard was an insightful and difficult experience in developing a useful tool tailored for myself. By diving into a expansive hobby I learned from a massive array of disciplines and got to understand, first hand, the potentials and difficulties of building and programming a custom keyboard. Fundamentally, the project can be split into hardware and software. 

The hardware component of the experience was significantly more creative and forgiving. Classmate responses noted “…the physical customization of a keyboard… shows how creative you can be with something that seems really standard.” I felt this was a perfect interpretation of my ideation stage, which saw me find all the possible inputs I could use without writing new fundamental components, like libraries. Unfortunately, my inexperience led to some hardware choices which, in retrospect, were not ideal. This is exemplified by my choice of a potentiometer instead of a rotary encoder. While the potentiometer worked well for volume control, a hardware item with a physical upper and lower limit was not as easy to apply to common computer programs as would have been a simple increase-decrease input (rotary encoder). This taught me that when designing a versatile tool, the more open ended and simple the inputs, the more use it will be.

A less functional, but still incredibly important aspect of the project is the design. A big motivation between custom keyboards is personalization. One classmate wrote “I like how clean the keypad looks with the acrylic base and it’s really nice how you hand-wired everything.” This comment made me very excited to see other people finding joy in customizing something so mundane. Through the project I learned about popular styles and where enthusiasts found beauty in electrical tools like keyboards. Hand-wiring and efficiency were popular themes that I subscribed to. Gridded geometric wiring, clear casing, and colorful keycaps were my adoption of the popular design principles in custom keyboards.

The software portion of the project was much more rigid and problematic. Here, roadblocks presented themselves left and right, from out of date tools to nuanced device issues. While there was a breadth of experience available, the lack of a single popular resource meant many uncommon issues remained unsolved. Other project documentation often warned of numerous attempts to get fundamental functionality.

Future iterations are all but set in stone. The giant variety of formats and the fact that I will probably use a keyboard every day for the rest of my life assures me this is not a learning experience I am done with. Layered cases, greater quantity of inputs, more complex inputs, even designing a marketable product are within the range of possibility.

Block Diagram:

 

Schematic Diagram:

Code:

//Accessory Keyboard
//The code maps the matrix wired into columns and rows to output keys or packets of information. It also attempts to create layers by taking advantage of arduino's capabilities over a traditional keyboard. Unforunately, this portion was not properly impletmented.
//The matrix assigns columns to pins 6, 7, 8, and 9. Rows are assigned to pins 4 and 5.
//The code borrows heavily from creators who have tried to execute similar projects and smaller projects that do fewer functions.
//https://create.arduino.cc/projecthub/Arnov_Sharma_makes/hid-volume-knob-with-pro-micro-db0bf8 by Arnov Sharma
//https://www.instructables.com/Programmable-Macropad-V2/ by tinyboatproductions

#include <Keypad.h>
#include <Encoder.h>
#include <Bounce2.h>
#include "HID-Project.h"
//The required libraries that simulate keyboard inputs
//Keypad buttons
int R1 = 4;
int R2 = 5;
int C1 = 6;
int C2 = 7;
int C3 = 8;
int C4 = 9;
//Necessary arrangement to help orient ourselves when receiving input
const byte ROWS = 2;
const byte COLS = 4;
char keys[COLS][ROWS] = {
  {'4','8'},
  {'3','7'},
  {'2','6'},
  {'1','5'}
};
byte rowPins[ROWS] = {R1, R2};
byte colPins[COLS] = {C1, C2, C3, C4};
Keypad kpd = Keypad( makeKeymap(keys), colPins, rowPins, COLS, ROWS);

// Unused code to allow for variable inputs based on unintegrated magnet switch
int S1 = 15;
int S2 = 14;
const int numStates = 2;
const int States[numStates] = {S1, S2};
int currentState = 0;

int lastDebounceTime = 0;
const int debounceTime = 50;

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

  for (int i = 0; i < numStates; i++){
    pinMode(States[i], OUTPUT);
    digitalWrite(States[i], LOW);
  }
  
  Keyboard.begin();
}

//Borrowed code that helps tell the arduino which state it is in. Also unused as only one state stays active.
void ChangeState(){
  digitalWrite(States[currentState], LOW);
  currentState++;
  if (currentState == numStates){
    currentState = 0;
  }
  digitalWrite(States[currentState], HIGH);
  delay(100);
  return;
}

// The first layout that can assign the 8 switch buttons to any keyboard output, or combination thereof. Note key 5 outputs a text packet.
void Layout1(char button){
  switch(button){
    case '1':
      Keyboard.print("1");
      break;
    case '2':
      Keyboard.print('2');
      break;
    case '3':
      Keyboard.print('3');
      break;
    case '4':
      Keyboard.print('4');
      break;
    case '5':
      Keyboard.print("Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not. It’s not a story the Jedi would tell you. It’s a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could use the Force to influence the midichlorians to create life… He had such a knowledge of the dark side that he could even keep the ones he cared about from dying. The dark side of the Force is a pathway to many abilities some consider to be unnatural. He became so powerful… the only thing he was afraid of was losing his power, which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep. Ironic. He could save others from death, but not himself.");
      break;
    case '6':
      Keyboard.print('6');
      break;
    case '7':
      Keyboard.print('7');
      break;
    case '8':
      Keyboard.print('8');
      break;
    case '9':
      Keyboard.print('9');
      break;
  };
}
void Layout2(char button){
  switch(button){
    case '1'://
      break;
    case '2'://
      break;
    case '3'://
      break;
    case '4'://
      break;
    case '5'://
      break;
    case '6'://Return
      Keyboard.press(KEY_RETURN);
      Keyboard.releaseAll();
      break;
    case '7'://Escape
      Keyboard.press(KEY_ESC);
      Keyboard.releaseAll();
      break;
    case '8'://
      break;
    case '9'://
      break;
  };
}
void loop() {
  //check the key matrix first
  char key = kpd.getKey();
  if(key) {
    switch(key){
      case '*':
        ChangeState();
        break;
      case '-':
        Keyboard.press(KEY_RIGHT_CTRL);
        Keyboard.press('s');
        delay(10);
        Keyboard.releaseAll();
        break;
      default:
        switch(currentState){
          case 0:
            Layout1(key);
            break;
          case 1:
            Layout2(key);
            break;
        }
    }
  }

 

]]>
Medicine Taking Reminder https://courses.ideate.cmu.edu/60-223/f2021/work/medicine-taking-reminder/ Tue, 02 Nov 2021 12:15:53 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14214

Final Project Full Image

Final Project Full Image 2

Project with medicine bottles for size

Overview

This project is meant to remind you on an interval to take your medication, but also let you know whether or not you have taken medication already to avoid double dosing. I often have a problem where I will forget whether or not I have already taken medication and in order to avoid double dosing, I will just not take medication that time. However, this absolutely leads to me to miss doses, and this project was meant to solve this issue.

Process Images and Review

The first design decision that I worked through was how to register the presence of the medication bottles within each container. My initial idea was to use a round force sensitive resistor to detect the bottles. As I designed and built this for my prototype, I realized that there were serious flaws with this idea, the first being the need for some way to concentrate the weight of the bottle onto the resistor. My solution, as shown in the image of my prototype, was to add a mushroom looking platform that sits on a small cylinder the size of the FSR. However, this was very finicky and unstable and inconsistent in registering presence.

The top circle cutout shows what the FSR looks like, and the lower image shows the platform sitting on top of the FSR. This was the prototype before I made the design decision.

Close up of the platform

Platform sitting within a medicine holder

I decided to change this setup to take advantage of the small IR distance sensors that use photo-resistors to register distance. This was far more accurate and solved any inconsistency issues I had.

Another updated design decision that I made regarding this device, is the change from 4 holes to 2 holes for medication. Given the cables required for sensors, LEDs, the joystick, and the LCD display, space became quite limited within the box itself. 4 was an unfeasible number of medication slots for the device to have, it would not have been possible unless I designed the entire device as a cube.

Discussion

One comment that I received during the peer feedback was “Joystick has many functions that simplify user interface” and I completely agree. My original idea was to use 5 different buttons for up/down/left/right/click actions, but realized that the Joystick is a single object that allows for all of those movements. This meant that I can in the future add as many user settings as possible, and use L/R to scroll between the settings and U/D to change the settings, and click to confirm or reset the device time.

Another comment that I received was “The light source could be a bit brighter, since it is a bit muted right now because of the opaque 3d printing material in the way.” and in hindsight, this would have been great to take into consideration. My problem with displaying whether medication had been taken was that finding a place to put the LED so that it is visible was difficult. What this comment touches on, is why do I not create a clear container to hold the medication, and perhaps use an opaque wall to separate the container from other containers and the internal components. This would have been a much prettier and more effective design.

Overall, I am fairly happy with where the project ended up, but there were absolutely improvements that would have made the end product better. My goal with this project was to learn to laser cut and design using CAD software, learn to 3D print, and create a viable method of reminding myself to take medication. Laser cutting and 3D printing was a step of the creation process that I spent a lot of time on and think that I am far more comfortable with now. While this is a viable way to remind myself to take medication, my peers were correct when saying that a simple 7 day physical container for medication would have been much simpler. That is one of the flaws of the project as a whole, in that it was completely unnecessary but a lot of fun to create.

While working on this project, I definitely learned that laser cutting and 3D printing is not quite the scary monster that I had always considered it. For some reason, I had always considered these two things intimidating and psyched myself out of learning both skills. This project and the number of iterations I needed to make on my project gave me comfort with cutting with the Rabbit machines. I also learned that while in the past I was very good at just jamming wires into wherever it would fit, when trying to work with a box and size constraint, proper wire management is important and is something that I should look into planning for future projects. During this project I realized that creating pieces and parts that fit well with each other is surprisingly soothing and therapeutic, and on the other hand frustrating when they do not fit together, and I can definitely see myself coming to make small trinkets to give my brain a breather.

I do not expect to build another iteration of this project, like mentioned above, because this project is very redundant in its functionality. There is a far simpler alternative that would fulfill the same purpose far more reliably. If I were to remake it, I would change the material used to 3D print to a clear material, turn the box into acrylic for the aesthetic, and include proper wire management into size planning and the internal layout.

Technical Information

Block Diagram

Schematic

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

/* 
Project Name: Medicine Taking Reminder
Nathan Shao
Description: This code powers the settings and timer functionality of the Medicine Taking Reminder Box. It allows a user to switch 
             settings by moving a joystick left and right, and modifying that setting using up and down, and resetting the timer
             by pressing down on the joystick.
Credit: Some pieces of code were taken from the 60-223 course website at https://courses.ideate.cmu.edu/60-223/f2021/tutorials/I2C-lcd

*/
LiquidCrystal_I2C screen(0x27, 16, 2);

int VRx = A0;
int VRy = A1;
int SW = 2;

int photoresist1Pin = A2;
int LED1 = 13;
int photoresist2Pin = A3;
int LED2 = 12;

int xPosition = 0;
int yPosition = 0;
int SW_state = 0;
int mapX = 0;
int mapY = 0;

long def = 43200000; // 12 hours

long timer1 = 0;
bool read1 = false;

long timer2 = 0;
bool read2 = false;

int curSetting = 0;
long adjust = 0;
long settingTimer = 0;
long curTime = 0;
long medTime1 = def;
bool taken1 = false;
long medTime2 = def;
bool taken2 = false;

void setup() {
  // initialize the screen (only need to do this once)
  screen.init();

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

  // set cursor to home position, i.e. the upper left corner
  screen.home();
  // print the words Hello, world! onto the screen starting at the above cursor position
  screen.print("Hello, world!");
  screen.clear();

  Serial.begin(9600);

  pinMode(VRx, INPUT);
  pinMode(VRy, INPUT);
  pinMode(SW, INPUT_PULLUP);

  pinMode(photoresist1Pin, INPUT);
  pinMode(LED1, OUTPUT);
  pinMode(photoresist2Pin, INPUT);
  pinMode(LED2, OUTPUT);

  curTime = millis();
}

// read the joystick
void readJoy() {
  xPosition = analogRead(VRx);
  yPosition = analogRead(VRy);
  SW_state = digitalRead(SW);
  mapX = map(xPosition, 0, 1023, -512, 512);
  mapY = map(yPosition, 0, 1023, -512, 512);
}

void processDisplay() {
  if (mapX > 300 && (millis() - settingTimer > 1000)) {
    // If the joystick moves left or right, change the setting currently selected
    curSetting = (curSetting + 1) % 2;
    settingTimer = millis();
  }
  else if (mapX < -300 && (millis() - settingTimer > 1000)) {
    curSetting = (curSetting + 1) % 2;
    settingTimer = millis();
  }
  // output updated selection and time to LCD
  screen.clear();
  screen.print("Med 1:");
  screen.print(medTime1 / 1000 / 60 / 60);
  screen.print(" hours");
  if (curSetting == 0) {
    screen.print("*");
  }
  screen.setCursor(0, 1);
  screen.print("Med 2:");
  screen.print(medTime2 / 1000 / 60 / 60);
  screen.print(" hours");
  if (curSetting == 1) {
    screen.print("*");
  }

  // If the joystick moves up or down, modify the selected setting by an hour each
  switch (curSetting) {
    case 0:
      if (mapY > 300 && (millis() - adjust > 500)) {
        medTime1 -= 3600000;
        adjust = millis();
      }
      else if (mapY < -300 && (millis() - adjust > 500)) {
        medTime1 += 3600000;
        adjust = millis();
      }
      break;
     case 1:
       if (mapY > 300 && (millis() - adjust > 500)) {
          medTime2 -= 3600000;
          adjust = millis();
        }
        else if (mapY < -300 && (millis() - adjust > 500)) {
          medTime2 += 3600000;
          adjust = millis();
        }
      break;
     default:
      break;
  }
}

// read the first IR sensor
void readIR1() {
  int readVal;
  readVal = analogRead(photoresist1Pin);
  if (readVal < 100 && !read1) {
    timer1 = millis() + 2000;
    read1 = true;
  }
  Serial.print(readVal);
}

// read the second IR sensor
void readIR2() {
  int readVal;
  readVal = analogRead(photoresist2Pin);
  if (readVal < 100 && !read2) {
    timer2 = millis() + 2000;
    read2 = true;
  }
  Serial.print(" ");
  Serial.println(readVal);
}


void loop() {
  readJoy();
  // if button press, reset the time
  if (SW_state == 0) {
    curTime = millis();
    taken1 = false;
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
  }

  processDisplay();

  readIR1();
  // update LED state as necessary
  if (millis() > timer1 && read1) {
    digitalWrite(LED1, HIGH);
    read1 = false;
  }

  readIR2();
  // update LED state as necessary
  if (millis() > timer2 && read2) {
    digitalWrite(LED2, HIGH);
    read2 = false;
  }

  // when time limit reached, reset LEDS.
  if (millis() > curTime + medTime1 && !taken1) {
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    taken1 = true;
    curTime = millis();
  }
  // when time limit reached, reset LEDS.
  if (millis() > curTime + medTime2 && taken1) {
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    curTime = millis();
    taken1 = false;
  }

  delay(100);
}

 

]]>
Mooonster Timer Box https://courses.ideate.cmu.edu/60-223/f2021/work/mooonster-timer-box/ Tue, 02 Nov 2021 06:32:59 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14339 The “Mooonster Timer Box” functions as a timer for a quick sketch/timed exam session.

Images

Overall Photo

Details: Wiring Inside the Box

Details: The welcome kitty smile when plugged in! (·v·)

In Use: Lit up ring in the dark during countdown

In Use: Lit up ring in the dark when the timer is up

Video Demonstration of the Usage

* Timer has been sped up for demo purposes! The timer only counted approx. 5 seconds although the timer is set to 1 minute

Process Images and Review

  • Decision Point #1: Discarding the Stepper Motor and “King Arthur” Decor

Original sketch, which features the stepper motor and King Arthur

The updated sketch for the final product before fabrication.

In the original ideation, I wanted to use a stepper motor to mimic the clock’s hands, and have it rotate to point to a ring of lit up lights as the timer goes down. However, I soon realized that the stepper motor can take up a large amount of space, which would cause trouble for fabrication, while providing merely decorative effects. So I decided to remove the stepper motor from the project.

The 3D printed decorative object was removed due to similar reasons. Initially, since the device requires the user to put back the pen once the timer is up, I wanted to name the project “Excalibur”, referencing the sword that cannot be pulled out and stays in place. So I wanted to model a “King Arthur” figure to “hold” the pencil in place. However, after starting the model for around 30 minutes, I realize the modeling of this merely decorative character would take significantly more time than I anticipated. Since this shouldn’t be the focus of the project, I decided to remove this component as well.

  • Decision Point #2: Fabrication through Laser Cutting instead of 3D Printing

The failed model for 3d printing. The geometry could not fom a valid manifold on the inner side of the box.

Laser cutting design for the four sides of the box

Laser cutting design for the box top/bottom

 

Initially I wanted to 3D print the outer box, giving it a smooth, plastic-like texture. However, after realizing that printing one small component could take one hour, I realize 3D printing is not the most efficient way, especially if I need to modify the appearance in any way. In addition, it turns out Maya isn’t exactly designed for fabrication modeling. Specifying a certain length in Maya was absolutely pain, and creating a manifold was also a problem. Therefore, I decided to switch to laser cutting the outside box, which turned out to be much more efficient.

  • Some More Process Images:

The sample code from NeoPixel library uses a for loop to light up each pixel, which requires the use of delay(). This image illustrates the process of figuring out how to loop through the lights on the NeoPixel ring correctly, so that they light up one by one without interfering the rest of the general loop.

This is the complete functional software prototype before the fabrication design began.

Roughly assembled fabrication with functioning software

Discussion

Overall, I was proud of the final product of this project, as the core functionality from the ideation was fully implemented, and the desired aesthetic design was achieved.

I was pleasantly surprised at how well the appearance turned out. Compared to physical fabrication, I was a lot more confident in the software implementation and electrical wiring. As someone with no experience with fabrication ever, I was dreading making the outer box for the project, and I was prepared for some degree of failure. But after warming up and experimenting with the laser cutting machine for a while, the process of fabrication went much smoother and quicker than I anticipated, and I really enjoyed it. I would definitely use this newly acquired skill in fabrication in the final project and some personal projects as well.

Regarding the in-class review and criticism, I totally agree with the following one: “It seems a bit bulky for something sitting on your desk.” This was my thought as well after assembling the box. I overestimated the space that the wiring would take up and made the box bigger than needed. Although it does not affect the appearance too much, it does cause some nuisance if I were to use this product daily, since it takes up a big chunk of space on my already crowded desk. In the future, I should definitely measure out the space the creation requires in detail, instead of just “eyeballing” how much space it might need.

I also appreciated the comments addressing the aesthetic design, such as “Really nice box and layout of components on the outside. Ah! A face! Cute” and “Looks elegant for your problem and would be a useful incentive to stop working since the lights are so bright.” One of the invited guests from that lecture also stared at the box for quite a while and told me that he “loves the little face”. The fact that the aesthetic design drew a lot of attention actually inspired me to improve the appearance a little bit more before documentation, adding a touch of yellow eyes and red cheeks to “humanize” the box even more.  (Special thanks to Nathan for the suggestion of adding the pink cheeks. Absolutely phenomenal. )

I might build a more compact version of this product in the future, with more compact wiring, a smaller Arduino board, and a reduced-sized outer box. This would be a lot more practical to be used more often. In addition, this time I would actually model the disposed “King Arthur”, just to experiment with 3D printing a little bit.

Functional Block Diagram

Schematic Diagram

Code

/*
 * Mooonster Timer Box
 * 
 * Creator: Bella Liu
 * 
 * Description:
 *   The project functions as a timer.
 *   The user can adjust the input time using the potentiometer.
 *   Once the time is adjusted, the user can press the start button to enter "timing session"
 *   The timer would then start, with the remaining time displayed on LCD screen.
 *   The ring of lights would light up, showing how much time has passed since the timer started.
 *   Once the count down is finished, the ring of lights would flash intense red lights,
 *   and the LCD screen would warn the user to put down the pen.
 *   The user must push the pen down the little hole on top to trigger the FSR, thus resetting the timer.
 *
 * Pin Mapping Table:
 * 
 *   Arduino pin | description
 *   ------------|-------------
 *   A0            FSR (output)
 *   A2            Potentiometer (input)
 *    2            Tactile Button Switch (input)
 *    6            NeoPixel Ring (input)
 *   SDA           SDA pin - LCD display (input)
 *   SCL           SCL pin - LCD display (input)
 *
 */


#include <Adafruit_NeoPixel.h>
#include <LiquidCrystal_I2C.h>

// Initializations for pins
const int FSR_PIN = A0;
const int POT_PIN = A2;
const int BUTTON_PIN = 2;
const int NEO_PIN = 6;

const int NUMPIXELS = 24; // Popular NeoPixel ring size

// The state of the tactile button
bool buttoned = LOW;
// Indicates whether the timer is in session
bool activated = LOW;
// The potentiometer value
int potVal = 0;
// The number of lights lit up in the ring
int lighted = 0;

// Variables
unsigned long lightTimer = 0;
const int lightWait = 500;

unsigned long LCDtimer = 0;
const int LCDwait = 500;
bool LCDstate = LOW;

unsigned long warningTimer = 0;
const int warningWait = 500;
bool warningState = LOW;

// Initialization of LCD and NeoPixel
LiquidCrystal_I2C screen(0x27, 16, 2);
Adafruit_NeoPixel pixels(NUMPIXELS, NEO_PIN, NEO_GRB + NEO_KHZ800);

void setup() {

  pixels.begin();
  pinMode(POT_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT);
  pinMode(FSR_PIN, OUTPUT);  
  Serial.begin(9600);
  screen.init();
  screen.backlight();

  //welcome smiley face
  screen.setCursor(7,1);
  screen.print("vv");   
  delay(5000);

  screen.home();
}

void loop() {
   
  buttoned = digitalRead(BUTTON_PIN);
  if (buttoned == HIGH) activated = HIGH;
  int minuteMin = 1;
  int minuteMax = 60;
  long interMin = 2500;
  long interMax = 150000;

  // Mapping the potentiometer value to 1-60 minutes
  int minute = map(potVal, 0, 1023, minuteMin, minuteMax);
  // Mapping 1-60 minutes to appropriate intervals for each light to light up
  unsigned int interval = map(minute, minuteMin, minuteMax, interMin, interMax); 
   
  //User is adjusting timer input
  if (activated == LOW)
  {
    pixels.clear();
    pixels.show();  
    lighted = 0;
    
    potVal = analogRead(A2);
    
    // Display the chosen time
    if ( (millis() - LCDtimer) >= LCDwait ) {
        screen.clear();
        screen.print("timer for:");
        screen.setCursor(0,1);
        screen.print(minute);      
        LCDtimer = millis();
    }
  
  }
  
  //The button has been pressed, enter "timing session"
  else
  { 
    pixels.clear(); // Set all pixel colors to 'off'

    unsigned long current = millis();

    //If time is up, stay in warning state
    if (lighted >= NUMPIXELS){

      // If FSR is triggered, exit warning state and return to user time adjustment phase
      if (analogRead(FSR_PIN) > 5) activated = LOW;
      
      if ( (millis() - warningTimer) >= warningWait ) {
        warningState = !warningState;
        warningTimer = millis();
      }    
        
      // Flash red light
      if (warningState == HIGH) {      
        for (int i = 0; i < NUMPIXELS; i++){
           pixels.setPixelColor(i, pixels.Color(255, 0, 0));
         }
      }
      pixels.show();

      // Display warning messages
      if ( (millis() - LCDtimer) >= LCDwait ) {
          screen.clear();
          screen.print("STOP! PEN DOWN!");        
          LCDtimer = millis();
      }
 
    }
   
    // For RGB values
    int green = 240;
    int blue = 0;    
    int red = 0;

    //If time is not up, light up the lights accordingly
    while (lighted < NUMPIXELS){
      
      if ( (millis() - lightTimer) >= interval) {

        // Change the color of the each light
        blue += 10;
        green -= 10;
        red += 5;
=
        pixels.setPixelColor(lighted, pixels.Color(red, green, blue));
        lighted ++;
        lightTimer = millis();
      }
      pixels.show();

      // Calculate the times
      int passedSec = ((millis()-current)/1000);
      int displaySec = passedSec % 60;
      int passedMin = passedSec / 60;

      // Display the timez on LCD screen
      if ( (millis() - LCDtimer) >= LCDwait ) {
          screen.clear();
          screen.print("Total");
          screen.setCursor(0,1);
          screen.print(minute);        
          screen.setCursor(6,0);
          screen.print("Counting");          
          screen.setCursor(6,1);
          screen.print(passedMin);
          screen.setCursor(8,1);
          screen.print(":");      
          screen.setCursor(9,1);
          screen.print(displaySec);          
          LCDtimer = millis();
      }      
    }    
  }   
}

 

]]>
Reusable Daily To Do List https://courses.ideate.cmu.edu/60-223/f2021/work/reusable-daily-to-do-list/ Tue, 02 Nov 2021 05:32:59 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14225 A reusable to do list that resets itself every morning to help you keep track of your daily tasks.

Photos

Final to do list – overall photo

Detail view of the separate components: the list, the stand, and the marker holder

Detailed view of the servo arms activating the switches and turning the LEDs back on

Demonstration of the whiteboard areas for easy editing and changing of daily tasks

View of the list in context, being placed by the bed for reminders in the morning and review at night

Process: Decision Points

One of the biggest decision points for me along this process was figuring out how I would reset the tasks each day. I had decided to use these manual switches in my design since I felt that turning them on and off reflected the same satisfaction of crossing something off a list, but this made it more difficult to reset the tasks compared to a button, as well as the challenge to scale up my design to accommodate five tasks. I contemplated several different methods such as a panel that could shift up and down to turn the switches back on, changing which side was “To Do” and which was “Done” each day, or finding a way to flip the switches internally, but these all had quite adverse effects on the human interaction with these switches, often compromising the ability to flip the switches manually. In my prototype and later my final design, I decided to use a servo arm to flip the switches manually since they worked consistently and did not interfere with the human interaction when not in use. While this was mechanically heavy and required many servos when applied to five tasks, I felt that this was the most efficient and effective method for my time.

Another major decision point in this process was what the layout of the items would be on the final product. This had to be done later in the process since it was largely driven by the side of the components relative to one another, but it was also the deciding factor in much of my final wiring layout and had to take that into consideration as well. While I had initially envisioned the board being taller than it was wide, being close to 8.5 by 11 paper dimensions, the dimensions for ideal writing space as well as the mechanics made it much shorter than I expected. In the end, I opted for square proportions as it seemed to fit five tasks (my average amount of daily tasks) nicely while also providing enough housing space for the internal components.

Process Images

My initial concept sketch at the bottom of the page had all the right components, but it was definitely a whole other challenge to determine the logistics and actually build it.

The highly simplified wiring diagram I made for myself before attempting to wire the same structure five times over.

With the same wiring repeated five times, it was important that I kept the wires as organized as possible, leading me to color code them (yellow for the servo, purple for the switches, blue for the LEDs, and so on).

Despite my careful planning of the wiring and implementation, some wires inevitably changed order, leaving my inputs and outputs quite jumbled. While the LEDs, servos, and switches sections stayed together, Switch 1 would turn off LED 4 and the servos would activate all out of order, making it quite hard to sort out. I decided to make the switch wiring the “driving” control by keeping that in place and changing the mapping of the LEDs and servos to match them.

Discussion

Overall, I am quite happy with how this project turned out! Coming from very little coding experience and no experience with electronics, I think I am quite proud of what I have been able to accomplish. The main goal I had for myself with this project, in the words of one of my professors, was to have “simple things done well” and choose something that would allow me to ensure it would be functional and resolved to the best of my ability. While I had some feedback regarding “a reward / punishment route” to provide motivation for these tasks, I wanted to focus more on what made traditional to-do lists helpful and satisfying for me and what works best for my lifestyle. I didn’t want to add a punishment system because I felt that would hinder the likelihood of me using this in the future, and I enjoy the passive, no pressure nature of to-do lists as they are now with more gentle reminders of what I should get done. While the code and wiring seemed like it would be quite simple and straightforward, it was surprisingly still quite the challenge, especially when doing things five times over! One of the biggest challenges for me was getting the mechanism of the servos to actually flip the switches, as I had spent many hours troubleshooting having them hit and go too far, being at the wrong height to hit and have enough force, having servos that did not work or were too weak, and so on. The main takeaway from that experience for me was how many different problems can show up with the same error and to be mindful that what worked to solve the problem last time may not work again in the same way. Despite these challenges, I think I learned a lot about making something “real” as in design we often stop our process at the concept and rarely resolve the finer details to make a model that works as intended. If I were to do this project again, knowing what I know now, I would love to implement some of the feedback that I got from my peers, such as “being to control each section”, as I think that would make the product much more refined by only switching on the ones that had been turned off.

Block Diagram

Schematic Diagram

Code

/*
    Reusable To Do List

    Description: This project creates a 5 switch task system with LEDs to signal whether or not they are
    flipped and the task is done, additionally with a real-time-clock plugin for servo motors to
    reset the switches and tasks at aspecified time each day.

    Resources Used:
    Real Time Clock: https://create.arduino.cc/projecthub/MisterBotBreak/how-to-use-a-real-time-clock-module-ds3231-bc90fe
    Mechanism Inspiration: https://create.arduino.cc/projecthub/viorelracoviteanu/useless-box-with-arduino-d67b47
*/

// PIN MAPPING
//  pin   | mode   | description
//  ------|--------|------------
//  2      input    Switch 1
//  3      input    Switch 2
//  4      input    Switch 3
//  5      input    Switch 4
//  6      input    Switch 5
//  8      ouput    LED 5
//  9      output   LED 4
//  10     output   LED 3
//  11     output   LED 1
//  12     output   LED 2
//  A0     output   Servo 4
//  A1     output   Servo 5
//  A2     output   Servo 3
//  A3     output   Servo 2
//  A4     output   Servo 1
//

//#include <Wire.h>
//#include <ds3231.h>
#include <Servo.h> // Servo library

//struct ts t;

Servo servo1; // one servo for each switch, top to bottom
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;

const int SERVOPIN1 = A4; //Mappings change to fit wiring of switches
const int SERVOPIN2 = A3;
const int SERVOPIN3 = A2;
const int SERVOPIN4 = A0;
const int SERVOPIN5 = A1;

const int SWITCHPIN1 = 2;
const int SWITCHPIN2 = 3;
const int SWITCHPIN3 = 4;
const int SWITCHPIN4 = 5;
const int SWITCHPIN5 = 6;

const int LEDPIN1 = 11;
const int LEDPIN2 = 12;
const int LEDPIN3 = 10;
const int LEDPIN4 = 9;
const int LEDPIN5 = 8;

unsigned long timerOne = 0; //Timer for demo mode reset

void setup() {
  Serial.begin(9600);
  //Wire.begin();
  //DS3231_init(DS3231_CONTROL_INTCN); //RTC setup
  //t.hour=9;
  //t.min=0;
  //t.sec=0;
  //t.mday=26;
  //t.mon=10;
  //t.year=2021;

  //DS3231_set(t);

  pinMode(SERVOPIN1, OUTPUT);
  pinMode(SERVOPIN2, OUTPUT);
  pinMode(SERVOPIN3, OUTPUT);
  pinMode(SERVOPIN4, OUTPUT);
  pinMode(SERVOPIN5, OUTPUT);

  pinMode (SWITCHPIN1, INPUT);
  pinMode (SWITCHPIN1, INPUT);
  pinMode (SWITCHPIN1, INPUT);
  pinMode (SWITCHPIN1, INPUT);
  pinMode (SWITCHPIN1, INPUT);

  pinMode (LEDPIN1, OUTPUT);
  pinMode (LEDPIN1, OUTPUT);
  pinMode (LEDPIN1, OUTPUT);
  pinMode (LEDPIN1, OUTPUT);
  pinMode (LEDPIN1, OUTPUT);

  servo1.attach(SERVOPIN1); // set up each servo on the corresponding data pin
  servo2.attach(SERVOPIN2);
  servo3.attach(SERVOPIN3);
  servo4.attach(SERVOPIN4);
  servo5.attach(SERVOPIN5);

  servo1.write(140); // servo reset to neutral position
  servo2.write(140);
  servo3.write(140);
  servo4.write(140);
  servo5.write(140);
}

void loop() {
  int switch1State;
  switch1State = digitalRead(SWITCHPIN1);
  int switch2State;
  switch2State = digitalRead(SWITCHPIN2);
  int switch3State;
  switch3State = digitalRead(SWITCHPIN3);
  int switch4State;
  switch4State = digitalRead(SWITCHPIN4);
  int switch5State;
  switch5State = digitalRead(SWITCHPIN5);


  if (switch1State == 1) {
    digitalWrite (LEDPIN1, HIGH); //Mapping switch state to LED state
  }
  if (switch1State == 0) {
    digitalWrite (LEDPIN1, LOW);
  }

  if (switch2State == 1) {
    digitalWrite (LEDPIN2, HIGH);
  }
  if (switch2State == 0) {
    digitalWrite (LEDPIN2, LOW);
  }

  if (switch3State == 1) {
    digitalWrite (LEDPIN3, HIGH);
  }
  if (switch3State == 0) {
    digitalWrite (LEDPIN3, LOW);
  }

  if (switch4State == 1) {
    digitalWrite (LEDPIN4, HIGH);
  }
  if (switch4State == 0) {
    digitalWrite (LEDPIN4, LOW);
  }

  if (switch5State == 1) {
    digitalWrite (LEDPIN5, HIGH);
  }
  if (switch5State == 0) {
    digitalWrite (LEDPIN5, LOW);
  }

  if (millis() - timerOne >= 30000) { //Refresh rate of screen every 30000 milliseconds
    // this is demo mode, in real mode: if (t.hour == 9 && millis - timerOne >= 3600000) { to occur once a day
    servo1.write(30); // tell the servo to go to 90º
    delay(200);
    servo1.write(140);
    delay(500);

    servo2.write(30);
    delay(200);
    servo2.write(140);
    delay(500);

    servo3.write(30);
    delay(200);
    servo3.write(140);
    delay(500);

    servo4.write(30);
    delay(200);
    servo4.write(140);
    delay(500);

    servo5.write(30);
    delay(200);
    servo5.write(140);
    delay(500);

    timerOne = millis();
  }
}

 

]]>
Nice Things Alarm https://courses.ideate.cmu.edu/60-223/f2021/work/nice-things-alarm/ Tue, 02 Nov 2021 05:02:05 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14333 Overview

An accessory to an alarm clock which prints out nice quotes in response to sensing the sound of an alarm.

A more in-depth video of the full functionality, including responsiveness to sound:

 

A video of the final project:

 

Featured image of the overall project.

 

Detail shot 1: Thermal printer which uses heat (as one might expect) to print text onto a roll of receipt paper.

 

Detail shot 2: SD card for storing large sets of data (text, in this case).

 

Detail shot 3: Microphone to sense input sound.

 

Usage shot 1: Alarm clock sits comfortably beside the box; with clock speakers next to the microphone.

 

Usage shot 2: Speaker against the clock.

Process images and review

One pivot point was deciding how to import a large-ish amount of data into the Arduino. Rather than storing all the string data on the Arduino’s tiny brain, I split my Google doc full of quotes (perpetually accumulating) into many little .txt files with a Python script.

Decision point 1: Quotes and a quote splitter.

Another pivot point was when I realized my dxf for the wooden box didn’t have a kern that allowed press-fitting. I considered recutting and perhaps modulating the design of the final product, but owing to the time constraints, I ended up rapidly locating wood glue and gluing my box together, hoping it would cure overnight (and it mostly did!)

Decision point 2: Having to construct a box with wood glue.

Additionally, here are a few more pictures of the process:

The first successful run of the printer!

 

…and a buggy run of the printer when trying to sort out multiple serial ports. It’s modern art?

 

The SVG for the graphics on top of the box that I then laser cut!

Discussion

“I wonder if there is a place that already has a “hub” of positive messages that you could source from, that way it could be more of a surprise for you?”

I really like the idea of the messages being pleasant surprises. However, I feel like the large hubs of positive quotes that I might find online would be those really cheesy, hashtag-inspirational ones that are nice but somewhat grating and simplistic. This does spawn another idea, though. I have most of my conversations with my partner over Discord (…haha) and Discord has a really solid and in-depth data request system, so I think I could do some text parsing to create little snippets from nice past conversations that I could randomly output every morning. That would fulfill all the personal, warm, and random categories.

“The printing format is super flexible and lets you print everything from jokes and riddles to memories and reminders.”

I like it too—I think if I were to continue developing this work, a large part would be increasing the diversity of the text repository. Another critiquer mentioned the receipts as a way to get news or weather so that he might check his phone less in the mornings, and I also like that intention.

Overall, I am moderately happy with how the project came out. I got burnt-out/sick during the middle of it, losing some momentum, and also realized that I would have to purchase ~$100 worth of materials (plus replenishing the receipt paper regularly) in order to keep using the project, which kind of deterred me from being as invested in the final product. I think the final product’s physical fabrication is a little shaky, and I would like to get better at this aspect of physical computing. However, I like the artistic implications of the project and the organic touches that I added to it. The process of crowdsourcing poetry and chopping it up into little bits was therapeutic, especially knowing that I would have something that I could hold in my hands at the end of it. Really though, my favorite parts of this project involved community, i.e., learning people’s favorite poems or handing out the “test cases” and seeing people’s faces light up with joy.

I found some limitations in knowledge when I spent an embarrassing amount of time in the laser room trying to piece my box together around my project. I would like to get better at CAD so I can create nicer, organic forms. I also hit some roadblocks with maintaining two separate serial communication lines (with the SoftwareSerial library, which turns any pair of digital pins into data RX/TX lines)—one for debugging, and another for the thermal printer. And I had a weirdly hard time trying to figure out how to convert ints to strings because it’s different for C, normal C++, and Arduino C++?

I am not sure if I want to invest in another, long-term version of this project, but if I were to, there are a few things I would do differently. I’d probably make the speaker a little more sturdy—perhaps panel mounted with a hood above it so it stays in place nicely and outside sound doesn’t interfere as much. I’d probably manipulate the strings a little better so words don’t break in the middle (the printer seems to simply split strings into new lines wherever the receipt isn’t wide enough). I’d also position the printer a bit more nicely, so the receipt paper is closer to the top of the box and feeding and tearing it out is easier.

Technical information

 

Block diagram

Schematic

Code:

/*------------------------------------------------------------------------
Copyright 2021 Shenai Chan

Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation 
the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the 
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included 
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
IN THE SOFTWARE.
--------------------------------------------------------------------------*/

/*------------------------------------------------------------------------
Description:
Data is read into the Arduino by way of a micro SD card reader and printed
onto a receipt with a thermal printer when the appropriate threshold of 
sound is reached (as detected by sound detector).
--------------------------------------------------------------------------*/

/*------------------------------------------------------------------------
Connections:
Printer RX              <-->  6 Output
Printer TX              <-->  5 Input
SD MOSI                 <--> 11 Input
SD MISO                 <--> 12 Input
SD CLK                  <--> 13 Input
SD CS                   <-->  4 Input
Sound Detector Gate     <-->  2 Input
Sound Detector Envelope <--> A0 Input
--------------------------------------------------------------------------*/

// Initializing the printer:

#include "Adafruit_Thermal.h"

// Creating a separate serial channel for the printer specifically
// to talk to the Arduino
#include "SoftwareSerial.h"
#define TX_PIN 6 // Arduino transmit  YELLOW WIRE  labeled RX on printer
#define RX_PIN 5 // Arduino receive   GREEN WIRE   labeled TX on printer

SoftwareSerial mySerial(RX_PIN, TX_PIN); // Declare SoftwareSerial obj first
Adafruit_Thermal printer(&mySerial);     // Pass addr to printer constructor

/*-----------------------------------------------------------------------*/

// Initializing the SD card:

#include <SPI.h>
#include <SD.h>

const int chipSelect = 4;

/*-----------------------------------------------------------------------*/

// Initializing the sound detector connections

#define PIN_GATE_IN 2
#define PIN_ANALOG_IN A0

/*-----------------------------------------------------------------------*/

// Timer variables

unsigned long timer = 0;
const int wait = 5000;

/*-----------------------------------------------------------------------*/

void setup() {

  mySerial.begin(19200);  // Initialize SoftwareSerial
  printer.begin();        // Init printer (same regardless of serial type)
  
  // Open serial communications and wait for port to open:
  Serial.begin(19200);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  // init pin for sound detector
  pinMode(PIN_GATE_IN, INPUT);
  
  delay(10000);

}

void loop() {
  int value;

  // Check the envelope input
  value = analogRead(PIN_ANALOG_IN);

  // Convert envelope value into a message
  Serial.print("Status: ");
  Serial.println(value);
  delay(1000);

  // If sound is loud enough
  if (value >= 20) {
    // and it's been long enough since the last printing
    if ((millis() - timer) >= wait) {
      timer = millis();
      int i = random(0, 19);

      // take a random .txt file
      String path = "text" + String(i, DEC) + ".txt";
      File myFile = SD.open(path);
      String text = "";

      // and print it
      if (myFile) { 
        while (myFile.available()) { //execute while file is available
          char letter = myFile.read(); //read next character from file
          text += letter;
        }
        myFile.close(); //close file
        printer.println("");
        printer.println(text);
        printer.println("");
      }
      else {
          Serial.println("error opening "+path);
      }
    }
  } 
}

 

]]>
Study Time Phone Jail https://courses.ideate.cmu.edu/60-223/f2021/work/study-time-phone-jail/ Tue, 02 Nov 2021 04:17:04 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14221 This device works to hold my phone while I am studying or doing work, preventing me from distracting myself by using my phone while I should be focusing on the task at hand.

Use the knobs to set the work time and break time before beginning the study session.

Place your phone in the casing to begin your study session. An FSR Pressure Sensor detects the presence of the phone. The timer will start to count down automatically.

Overall Photo

The device holds your phone while you work for an amount of time set by the user. When time is up, a set break will begin, allowing the user to take their phone without penalty.

Detail Photos

The timer counts down on the LCD Screen before automatically switching to the break timer.

When the door is closed, the time and the top notification is still visible to the user, in case the user receives a notification they believe is an emergency.

Usage Photos

The user may open the door to quickly check their phone. However, if the photoresistor to the right of the phone detects that the door is open for more than 10 seconds, an alarm triggers and the message shown above is displayed on the LCD screen.

 

If the phone is removed before the break begins, the device will set off an alarm once again until the phone is put back. The timer doesn’t count down in this state.

 

Here is a picture of my device in action while I write this report!

Process and Review

During this project, I had to make many decisions that significantly changed the outcome of my design and project as a whole.

One challenge I ran into early was that my phone did not activate the pressure sensor. My whole idea thus far had relied on the phone activating the pressure sensor, and this realization frustrated me early on. With some testing, I knew that the pressure sensor did work when I used my finger to apply pressure, just not when my phone rested on top of it.

As a result, I concluded that the pressure applied needed to be concentrated. Since my phone is wider than the sensor, the weight of the phone was too distributed to impact the resistance in the FSR.

To solve this problem, I created the following 3D printed part to hold up a wide body, and concentrate its weight in a smaller location:

This part concentrates pressure due to weight in a small area. The part was 3D printed using the IDeATe Skylab.

Since the phone still could not rest solely on this part, I had to attach it to the piece of wood that would act as the casing for the phone. In order to prevent the assembly from falling over, I used springs in each of the four corners for stability. This worked well to hold up the corners and allow for slight vertical displacement when the phone applies pressure to the assembly, which then applies pressure to the FSR sensor, activating my device.

This design allows for stability of the phone and activation of the FSR sensor when the phone is placed into the device.

The assembly was placed in the middle of the box in a strategic spot to avoid other wires and electrical components.

 

Another challenge I ran into during the design process was what to do with the door that covered my phone during work time. Since I cut the entire box out of wood, the axels that the door hinges on are actually rectangular. To make them fit in the holes of the hinge, I had to sand the corners of the rectangular cut out until they allowed the door to open and close smoothly. I am proud of this work around, as it allowed me quickly adapt my prior work with out the use of additional materials or design components.

I knew I wanted to have some visibility so that the user could identify if a notification was an emergency that needed a response. However, with the door there, there was nothing discouraging the user from just opening the door to use their phone while still activating the pressure sensor. To combat this, I wanted to somehow sense if the door was closed or not to punish the user when the door was open for too long by ringing the alarm that sounds when the phone is removed from the device.

I felt that this discouragement was sufficient enough for me, as the buzzer used makes a really loud sound at a pitch I am fairly sensitive to. I will defiantly be motivated to close the door as soon as it starts going off.

I wrestled with a few ideas on how to sense if the door was closed or not. I ended up using a photoresistor to detect a change in light brightness when the door closes. The photoresistor would stick out on the top of the box, next to the phone as shown below. This way it is covered when the door is closed and is un-intrusive to the user.

The photoresistor peeks out from a hole drilled into the top of my casing.

To wire and attach the photoresistor to the top of my box, I soldered it to a protoboard, along with the rest of the wiring needed to power the photoresistor.

A resistor, output connection, power and ground are soldered to the protoboard attached under the top of the box. Tape used early on.

Despite my efforts, the photoresistor value of the door being closed was inconsistent in different lighting environments since the door was not completely flush with the top of my box. To combat this, I glued some scrap wood on the edge of the door until it was flush with the top of my box, over the photoresistor. I also placed black electrical tape on the surface of the scrap wood to further darken the area the photoresistor sensed when closed. This solution worked well and made the values produced by the photoresistor much more consistent despite the brightness of the room.

Additional Process Pictures

In the prototype stages, only my finger could activate the pressure sensor, not my phone. Nonetheless, I was proud of myself for wiring and coding the interaction between the potentiometer knobs, FSR, Buzzer and LCD Screen without additional assistance.

I was challenged to organize the wiring and components in the box in an efficient and effective way.

While I knew that soldering all of the electrical components would make better connections, I ended up doing so sparingly. While all wiring for the FSR sensor and the photoresistor are soldered in the final product, I determined it was unnecessary to solder the potentiometers and the buzzer. Since I am a novice at soldering, I did not want to risk damaging the potentiometers during that process, a consequence that can occur if done without precision. Also, the breadboard provided a great base that helped control the height of the potentiometer, which helped when designing the box to encase them in. Since the box only sits on my desk and the wires are not intended be thrown around, I deemed it less important to solder each component, and used that time to improve the ergonomics and sleek design of my device.

Ensuring electrical connections were sufficient was a big challenge and required careful consideration of elegance, safety, and my time.

Discussion

When presenting my project to the class, I received some great critiques and feedback. One piece of feedback I received was that I should have had the buzzer quickly go off to alarm the user that they have transitioned into the break time from the work time. This is a great idea and something that I had originally thought of during the prototyping phase. However, I ended up forgetting to incorporate the idea, and it completely slipped my mind until I was given feedback. Additionally, it was suggested that I added an emergency stop to my device, so that it would not alarm when I discontinued use in the event of an emergency. While I also considered this, I decided that if there was a true emergency, I could just unplug the device from my computer. I know that I would not abuse this power, as it defeats the whole purpose of what I am trying to do. If my phone is in my assistive device, it will remind me of my goal to keep working until the break. Despite these minor shortcomings, I am proud of the project that I created. Not having a partner who knew electronics left me responsible for figuring out the unknown by myself and challenged me to learn and grow as an engineer. I exceeded my personal expectations and I believe that I delivered a complete and robust project. I learned so much during this project, including a better understanding of electrical components, soldering, simple algorithms and how they are integrated with mechanical design. The project challenged me to think about problems using different perspectives and challenged me to solve those problems in a creative and elegant way. While it is safe to say that this will be the final version of my device for now, there are a few modifications that I could make to clean up the user experience. For example, the buzzer indicating the transitions as mentioned above. I would also like to add a button that takes the user back to the home screen to let them set new work and break time durations without having to unplug the device. These additions would be nice, but are not significant enough to require iteration in order to use the device. It works well for me and my need!

Block Diagram

The block Diagram generalizes the electric schematic of the assistive device. This is helpful for early ideation and visualizing the flow of the wiring.

Electrical Schematic

The schematic better represents the wiring of my project including which pins on the Arduino I used.

Code

/* Study Time Phone Jail
   By Matthew Wagner
   60-223 Introduction to Physical Computing
   Professor Robert Zacharias
   Due 11/2/2021
  
   This project helps someone stay focused when studying by 
   trapping their phone in the 'Jail', preventing use. The 
   user can set how long they want to work and take a break
   for. While working, if the phone is removed, or the door
   cover is opened for too long, the device alarms the user
   to return to their task and put the phone back. The user
   gets a set break time after each work time.
  
   Pin mapping:
     Arduino pin | role   | description
     ------------|--------|-------------
     A0            input    potentiometer (Sets Work Time)
     A1            input    potentiometer (Sets Break Time)
     A2            input    FSR Pressure Sensor (Square)
     A3            input    photoresistor
     13            output   active buzzer
     SDA           output   LCD Screan (modified)
     SCL           output   LCD Screen (modified)

     I^2C LCD: Modified code from Robert Zacharias
               Used Library by Frank de Brabender, 
               based on work from DFRobo (Nov. 2018)

     millis:   Modified code from Robert Zacharias
 */


/////////////////////////////
//        LIBRARIES        //
/////////////////////////////
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// create LCD Display called "screen"
LiquidCrystal_I2C screen(0x27, 16, 2);

byte frowny[] = {
  B00000,
  B11011,
  B11011,
  B00000,
  B00000,
  B01110,
  B10001,
  B00000
};


// track time passed using: millis()
const unsigned long DISPUPDATETIME = 500;
unsigned long lastUpdate = millis();


// FSR
const int FSRPIN = 2;
int fsrReading;

// photoresistor
const int PHOTOPIN = 3;
int photoVal;

// active buzzer
const int BUZZERPIN = 13;
int screenPeekTime = 0;

// User interface initial values/ settings:
int workTime = 45;
int breakTime = 5;
int seconds = 0;
bool phoneSensed = false;
bool working = false;
bool onBreak = false;
bool buzzerOn = false;
bool screenCoverClosed = false;

void setup() {
  // work time potentiometer
  pinMode(A0, INPUT);
  
  // break time potentiometer
  pinMode(A1, INPUT);

  // buzzer
  pinMode(BUZZERPIN, OUTPUT);
  digitalWrite(BUZZERPIN, LOW);

  // photoresistor
  pinMode(PHOTOPIN, INPUT);
  
  // initialize the screen (only need to do this once)
  screen.init();

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

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

void loop() {

  // let user set work and break time before starting work
  if (!working && !onBreak){
    // read potentiometer value, map to work time
    workTime = map(analogRead(A0), 0, 1023, 6, 36);
    // multiply by 5 to only display 5 minute increments between
    // 30 mins and 3 hrs
    workTime = 5 * workTime;
  
    // read potentiometer value, map to break time
    breakTime = map(analogRead(A1), 0, 1023, 1, 6);
    // multiply by 5 to only display 5 minute increments between
    // 5 and 30 mins
    breakTime = 5 * breakTime;

    if (millis() - lastUpdate >= DISPUPDATETIME){

      screen.clear();
  
      // display work time
      screen.print("Work Time: ");
      screen.setCursor(11, 0);
      screen.print(workTime);
      screen.print("m");
  
  
      // display break time
      screen.setCursor(0, 1);
      screen.print("Break Time: ");
      screen.setCursor(12, 1);
      screen.print(breakTime);
      screen.print("m");
      
      screen.home();
      
      lastUpdate = millis();
    }
  }

  // keep a timer while working
  else if (working && !buzzerOn && millis() - lastUpdate >= 2 * DISPUPDATETIME){
    screen.clear();

    // display time
    screen.print("Time Until Break");
    screen.setCursor(5, 1);
    screen.print(workTime);
    screen.print(":");
    if (seconds < 10){
      screen.print("0");
    }
    screen.print(seconds);
    screen.home();

    // update timer
    if (seconds == 0){
      if (workTime == 0) {
        working = false;
        onBreak = true;
      }
      else{
        workTime = workTime - 1;
        seconds = 59;
      }
    }
    else{
      seconds = seconds - 1;
    }

    // check if user is peeking
    if (!screenCoverClosed){
      screenPeekTime = screenPeekTime + 1;
    }
    lastUpdate = millis();
  }


  // keep a timer while on break
  else if (onBreak && millis() - lastUpdate >= 2 * DISPUPDATETIME){
    screen.clear();

    // display time
    screen.print("On Break for:");
    screen.setCursor(5, 1);
    screen.print(breakTime);
    screen.print(":");
    if (seconds < 10){
      screen.print("0");
    }
    screen.print(seconds);
    screen.home();

    // update timer
    if (seconds == 0){
      if (breakTime == 0) {
        // back to work when break is over
        working = true;
      }
      else{
        breakTime = breakTime - 1;
        seconds = 59;
      }
    }
    else{
      seconds = seconds - 1;
    }
    
    lastUpdate = millis();
  }
  
  // sensor
  fsrReading = analogRead(FSRPIN);
  
  if(fsrReading > 500){
    phoneSensed = true;
    digitalWrite(BUZZERPIN, LOW);
    buzzerOn = false;
    if (!working && !onBreak){
      working = true;
      onBreak = false;
      seconds = 0;
    }
    // reset work time and break time for next session after break
    else if (working && onBreak){
      onBreak = false;
      // read potentiometer value, map to work time
      workTime = map(analogRead(A0), 0, 1023, 6, 36);
      // multiply by 5 to only display 5 minute increments between
      // 30 mins and 3 hrs
      workTime = workTime = 5 * workTime;
    
      // read potentiometer value, map to break time
      breakTime = map(analogRead(A1), 0, 1023, 1, 6);
      // multiply by 5 to only display 5 minute increments between
      // 5 and 30 mins
      breakTime = 5 * breakTime;
    }

    // buzz if user is peeking for too long
    else if (working && !screenCoverClosed && screenPeekTime >= 12){
      digitalWrite(BUZZERPIN, HIGH);
      buzzerOn = true;
      if (millis() - lastUpdate >= DISPUPDATETIME){
        screen.clear();
        screen.print("CLOSE COVER NOW!");
        screen.setCursor(0,1);
        screen.print("NO PEEKING!");
        screen.home();
        lastUpdate = millis();
      }
      }
  }
  else{
    phoneSensed = false;
    // buzz if break ends and phone isn't sensed
    if (working && onBreak){
      digitalWrite(BUZZERPIN, HIGH);
      buzzerOn = true;
      if (millis() - lastUpdate >= DISPUPDATETIME){
        screen.clear();
        screen.print("BREAK IS OVER!");
        screen.setCursor(0,1);
        screen.print("PUT PHONE BACK!");
        screen.home();
        lastUpdate = millis();
      }
    }
    // buzz if phone isn't sensed while working
    else if (working && !onBreak){
      digitalWrite(BUZZERPIN, HIGH);
      buzzerOn = true;
      if (millis() - lastUpdate >= DISPUPDATETIME){
        screen.clear();
        screen.print("NO!!! IT IS NOT");
        screen.setCursor(0,1);
        screen.print("BREAK TIME YET");
        screen.home();
        lastUpdate = millis();
      }
      }
  }

  // read photoresistor
  photoVal = analogRead(PHOTOPIN);
  if (photoVal <= 300){
    screenCoverClosed = true;
    screenPeekTime = 0;
    digitalWrite(BUZZERPIN, LOW);
    buzzerOn = false;
  }
  else {
    screenCoverClosed = false;
    
  }
  }
  
  
  
  

 

]]>
Task Buddy https://courses.ideate.cmu.edu/60-223/f2021/work/task-buddy/ Tue, 02 Nov 2021 00:18:41 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14284

Overview

“Task Buddy” is a Tamagotchi-style daily task manager, where completing daily tasks contributes to the overall health of a virtual, portable pet.

Detail: Front design of the box

Buddy in use

All animation frames of the Buddy used

Usage Videos

Idle animation while Buddy is happy:

 

Idle animation while Buddy is sad, sped up 2x:

 

Adding a Task:

 

Deleting a Task:

 

You can set the Buddy to sleep, and then wake it up (with a penalty!):

 

When the timer for a task runs out, it autodeletes and there’s a penalty on either health or happiness:

 

If happiness or health is at zero for too long, the Buddy dies. Revivable with a penalty:

Process Images

First thing I did: got the OLED screen working with the buttons.

After getting the screen working, I spent a large portion of my time programming the game logic and each of the screens, working out how tasks would be added in and deleted, and how I would affect the Buddy. I also created each of the animation frames in this time.

Prototyping the wiring for the buttons, vibration motor, and speakers

Attaching the buttons to the board

Soldering it all together

More soldering done

After finishing all the wiring, all I needed was a case. Originally, I planned to 3D print a case, but the plan didn’t pan out. As a result…

Lasercut the final box and casing to put the entire setup within!

Discussion

“Screen is really small, making text hard to read (I’m old)” Agreed! One of the main issues I originally had with the whol”e thing was finding a suitable screen; before I found the OLED, I was going to just use blinking lights and other indicators. With the introduction of the OLED screen and its limitation, I tried to create something focused around the size of this screen, so it would look proportional in relation to it. However, I wasn’t able to make the device as small as I liked, and so the screen feels very small in comparison to the whole thing.

“Really letting the cute thing die.” Assuming this is a criticism on having there be stronger consequences for the buddy dying, I considered having a sort of randomly generated new buddy entirely different from the original or having the program keep track of number of deaths and make it harder to keep the buddy alive with each death, but I ended up scraping both ideas due to time.

I’m fairly happy with how the project turned out, especially in aspects of figuring out how to gamify the process of finishing tasks. However, I would improve it on many different fronts. For example, I would have liked to shrink the size of the casing even more and make it more portable, since currently it is a little unwieldy. I’d also like to add even more kinds of interactions by connecting it to an SD card, and perhaps allow the whole thing to be completely independent of external power sources, instead powering it with something like a battery. In particular, I learned how tricky the process of fabrication is, and to devote more time to it in the future.

If I built another iteration, I think I would focus on shrinking the design of it even further, spending more time mapping out all the wiring and able to figure out how to utilize my space most efficiently.

Technical information

Block Diagram and Schematic

Main Code File:

/*
 * Task Buddy
 * 
 * This code does all the animations, takes in inputs from 
 * buttons, and outputs speaker sounds and vibrations 
 * accordingly. 
 * 
 * PINS: 
 * button 1 = D4;
 * button 2 = D3;
 * button 3 = D2;
 * speaker = D5;
 * motor = D6;
 * 
 * Thank you to: 
 * This tool for converting images to byte arrays for the OLED:
 * https://javl.github.io/image2cpp/ 
 * 
 */
#include "U8glib.h"
#include "pitches.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST);  // Fast I2C / TWI

const unsigned char happy_happy0 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 
  0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 
  0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x80, 0x00, 
  0x00, 0x07, 0xdf, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 
  0xff, 0xc7, 0xff, 0xfe, 0x00, 0x01, 0xff, 0xff, 0x87, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xcf, 
  0xff, 0x7f, 0x80, 0x03, 0xf3, 0xff, 0xff, 0xff, 0x8f, 0x00, 0x03, 0xe1, 0xff, 0xff, 0xff, 0x86, 
  0x00, 0x01, 0xc1, 0xef, 0xff, 0xdf, 0x80, 0x00, 0x00, 0x01, 0xf1, 0xff, 0x1f, 0x80, 0x00, 0x00, 
  0x03, 0xf8, 0xfe, 0x3f, 0x80, 0x00, 0x00, 0x07, 0xfe, 0x01, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 
  0x03, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xdc, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 
  0xee, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe6, 0x00, 
  0x00, 0x67, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x67, 
  0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 
  0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 
  0x00, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 
  0xff, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3f, 
  0x80, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x1f, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy1 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 
  0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x01, 
  0xff, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xdf, 0xff, 
  0xff, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xc7, 0xff, 0xfe, 
  0x00, 0x01, 0xff, 0xff, 0x87, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xcf, 0xff, 0x7f, 0x80, 0x03, 
  0xf3, 0xff, 0xff, 0xff, 0x8f, 0x00, 0x03, 0xe1, 0xff, 0xff, 0xff, 0x86, 0x00, 0x01, 0xc1, 0xef, 
  0xff, 0xdf, 0x80, 0x00, 0x00, 0x01, 0xf1, 0xff, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf8, 0xfe, 0x3f, 
  0x80, 0x00, 0x00, 0x07, 0xfe, 0x01, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0x03, 0xff, 0x80, 0x00, 
  0x00, 0x07, 0xff, 0xff, 0xff, 0xdc, 0x00, 0x00, 0x27, 0xff, 0xff, 0xff, 0xee, 0x00, 0x00, 0x67, 
  0xff, 0xff, 0xff, 0xe6, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe6, 0x00, 0x00, 0x67, 0xff, 0xff, 
  0xff, 0xe0, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x67, 0xff, 0xff, 0xff, 0xe0, 
  0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 
  0x07, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x03, 0xff, 
  0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf8, 
  0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xc7, 0x00, 0x00, 
  0x00, 0x00, 0x30, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x00, 
  0xfe, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy2 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
  0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfc, 
  0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xff, 0xfc, 0x00, 0x00, 
  0x00, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x7e, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x7f, 
  0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 
  0xff, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, 
  0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 
  0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xbf, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 
  0x7f, 0xbf, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x3f, 
  0xf0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9f, 0xc0, 0x00, 
  0x00, 0x00, 0xff, 0xf3, 0xcf, 0x80, 0x00, 0x00, 0x01, 0xff, 0xf3, 0x80, 0x00, 0x00, 0x00, 0x01, 
  0xff, 0xfb, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf9, 0xcf, 0x80, 0x00, 0x00, 0x01, 0xff, 0xf9, 
  0xcf, 0x80, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x1f, 0x80, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x3f, 0x9e, 
  0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x8e, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x80, 0x00, 0x00, 
  0x03, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x0f, 0x8f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x0f, 0xc1, 
  0xff, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0xff, 0xc3, 0x80, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x07, 
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy3 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xfe, 
  0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xff, 0xfe, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0x8f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1f, 
  0xff, 0xfe, 0xff, 0x80, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x1f, 0xff, 0xff, 
  0xff, 0x80, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 
  0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 
  0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x3f, 
  0xbf, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0x9f, 
  0xf8, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x9f, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xcf, 0xe0, 0x00, 
  0x00, 0x00, 0x7f, 0x3f, 0xe7, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 
  0xfc, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xe7, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xf9, 0xcf, 
  0xff, 0xc0, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x3f, 0xff, 0xcf, 
  0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xc7, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xc0, 0x00, 0x00, 
  0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x03, 0xc7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xe0, 
  0xff, 0xf0, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x7f, 0xe1, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 
  0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy4 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 
  0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x73, 0xff, 0xf0, 
  0x00, 0x00, 0x00, 0x00, 0x61, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xfe, 0x00, 0x00, 
  0x00, 0x70, 0x7c, 0xf9, 0xff, 0xf8, 0x00, 0x00, 0xf8, 0xff, 0xf0, 0xff, 0xfc, 0x00, 0x00, 0xff, 
  0xff, 0xee, 0x7f, 0xff, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 
  0xfb, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf1, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 
  0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 
  0x3f, 0xff, 0xdf, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x3f, 0xde, 0x00, 0x00, 0x00, 0x03, 0xfc, 
  0x7f, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf2, 
  0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf6, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xef, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x38, 0x00, 0x00, 0x03, 
  0xff, 0xff, 0xff, 0x38, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 
  0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 
  0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 
  0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x08, 
  0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1e, 
  0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy5 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 
  0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x3f, 0xfc, 
  0x00, 0x00, 0x00, 0x18, 0x39, 0x9f, 0xfe, 0x00, 0x00, 0x00, 0x3c, 0x39, 0x9f, 0x3f, 0x00, 0x00, 
  0x00, 0x7e, 0x3b, 0xde, 0x1f, 0x80, 0x00, 0x00, 0x7f, 0xff, 0xfc, 0xcf, 0xe0, 0x00, 0x00, 0x7f, 
  0xff, 0xfc, 0xcf, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xfd, 0xef, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 
  0xfd, 0xff, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0xf9, 0xff, 0x00, 0x00, 0x0f, 0xe1, 0xff, 0xfd, 0xff, 
  0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7e, 0x00, 0x00, 
  0x00, 0x00, 0xff, 0xff, 0x3c, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 
  0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 
  0xc0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xf9, 0xc0, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0xf9, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfc, 0xe0, 0x00, 0x00, 0x01, 
  0xff, 0xff, 0x9e, 0x60, 0x00, 0x00, 0x02, 0xff, 0xff, 0xce, 0x64, 0x00, 0x00, 0x02, 0x7f, 0xff, 
  0xe0, 0xe0, 0x00, 0x00, 0x02, 0x7f, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xc0, 
  0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 
  0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x18, 
  0xff, 0xf1, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x03, 
  0xe0, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy6 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x03, 
  0xff, 0x9f, 0xff, 0xc0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x3f, 0xff, 0xff, 
  0xfe, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xbf, 0xff, 0xc0, 0x00, 
  0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x78, 
  0xff, 0xff, 0xfc, 0x3c, 0x00, 0x00, 0xfd, 0xff, 0xff, 0xfc, 0x7c, 0x00, 0x01, 0xff, 0xff, 0xff, 
  0xfe, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0x1f, 0xff, 0x80, 
  0x00, 0x00, 0x7f, 0xfe, 0x4f, 0xff, 0x00, 0x00, 0x00, 0x3f, 0xfc, 0xcf, 0xff, 0x80, 0x00, 0x00, 
  0x0f, 0xf1, 0xe7, 0xff, 0x80, 0x00, 0x00, 0x07, 0xe3, 0xf3, 0xff, 0xc6, 0x00, 0x00, 0x00, 0x1f, 
  0xf3, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xf3, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xf3, 0xff, 
  0xe0, 0x00, 0x00, 0x00, 0xff, 0xf3, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xe7, 0xff, 0xc0, 0x00, 
  0x00, 0x00, 0xff, 0xcf, 0xff, 0x9f, 0xc0, 0x00, 0x00, 0x7f, 0x1f, 0xff, 0x9f, 0xc0, 0x00, 0x00, 
  0x00, 0x3f, 0xff, 0x8f, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x07, 0x80, 0x00, 0x00, 0x3f, 0xff, 
  0xfe, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 
  0x00, 0x00, 0x00, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char happy_happy7 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 
  0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x00, 
  0xf8, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x87, 0xf8, 0x00, 0x00, 0x70, 0x37, 0xff, 0xff, 0xf8, 0x00, 
  0x00, 0x78, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 
  0xff, 0xfd, 0xfe, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 
  0xfc, 0x00, 0x00, 0x00, 0x3f, 0x83, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf3, 0xfc, 0x00, 0x00, 
  0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 
  0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 
  0xc0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0x38, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 
  0x0f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 
  0xff, 0x20, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x60, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x70, 
  0x00, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x78, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xf8, 0xf8, 0x00, 0x00, 
  0x00, 0x3f, 0xff, 0xf1, 0xf8, 0x00, 0x00, 0x03, 0xc7, 0xff, 0x80, 0xf0, 0x00, 0x00, 0x03, 0xe0, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 3328)
const unsigned char* happy_allArray[8] = {
  happy_happy0,
  happy_happy1,
  happy_happy2,
  happy_happy3,
  happy_happy4,
  happy_happy5,
  happy_happy6,
  happy_happy7
};

const unsigned char sad_sad0 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 
  0x80, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf8, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xfe, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x7f, 
  0xff, 0x03, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x87, 0x9f, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 
  0x9f, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xcf, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe7, 0xff, 
  0x00, 0x00, 0x7f, 0xff, 0xff, 0xf1, 0xfe, 0x00, 0x00, 0x3f, 0xfc, 0xff, 0xf8, 0x00, 0x00, 0x00, 
  0x1f, 0xf8, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 
  0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 
  0xf0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xfe, 0xe7, 0xf0, 0x00, 
  0x00, 0x00, 0xff, 0xff, 0x73, 0xf3, 0x00, 0x00, 0x00, 0xff, 0xff, 0x33, 0xf3, 0x00, 0x00, 0x00, 
  0xff, 0xff, 0x33, 0xf3, 0x00, 0x00, 0x00, 0xff, 0xff, 0x87, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xff, 
  0xcf, 0xc0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x00, 
  0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xc0, 
  0x00, 0x00, 0x00, 0x00, 0x01, 0xe7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc7, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad1 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x3f, 
  0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 
  0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x00, 
  0x00, 0x7f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x7f, 0xfc, 0x00, 0x00, 0xff, 
  0xff, 0xc0, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xf9, 0xe1, 0xff, 0xff, 0x00, 0x00, 0xff, 0xf9, 0xff, 
  0xff, 0xff, 0x00, 0x00, 0xff, 0xf3, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xe7, 0xff, 0xff, 0xff, 
  0x00, 0x00, 0x7f, 0x8f, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x3f, 0xfc, 0x00, 0x00, 
  0x00, 0x7f, 0xfe, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 
  0xff, 0xe0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 
  0xc0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xfb, 0x9f, 0xc0, 0x00, 
  0x00, 0x03, 0xff, 0xfd, 0xcf, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xfc, 0xcf, 0xc0, 0x00, 0x00, 0x03, 
  0xff, 0xfc, 0xcf, 0xd8, 0x00, 0x00, 0x03, 0xff, 0xfe, 0x1f, 0xbc, 0x00, 0x00, 0x03, 0xff, 0xff, 
  0x3f, 0x3c, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x3c, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfc, 0x00, 
  0x00, 0x00, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 
  0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8f, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x07, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad2 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 
  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe1, 0xc0, 0x00, 
  0x00, 0x03, 0x03, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xf7, 0xf0, 0x00, 0x00, 0x1f, 
  0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xe1, 
  0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xf7, 0xff, 0xdf, 
  0xc0, 0x03, 0xf8, 0xff, 0xff, 0xff, 0xe3, 0x80, 0x03, 0xe0, 0xff, 0xdf, 0x7f, 0xe0, 0x00, 0x01, 
  0xc0, 0xff, 0x8e, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0xff, 0x71, 0xdf, 0xe0, 0x00, 0x00, 0x01, 0xff, 
  0xfb, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 
  0xe6, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xf7, 0x00, 0x00, 0x19, 0xff, 0xff, 0xff, 0xf3, 0x80, 
  0x00, 0x39, 0xff, 0xff, 0xff, 0xf3, 0x80, 0x00, 0x39, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x39, 
  0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x39, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 
  0xff, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 
  0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 
  0x00, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x3f, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 
  0xc0, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x07, 0xe0, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad3 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 
  0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 
  0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0x0e, 
  0x00, 0x00, 0x00, 0x01, 0xc1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xe3, 0xff, 0xff, 0x80, 0x00, 
  0x00, 0x07, 0xff, 0xff, 0xef, 0xc0, 0x00, 0x00, 0x0f, 0xdf, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 
  0xff, 0xc7, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xc3, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xff, 0xe7, 
  0xff, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0xfe, 0x7f, 0xff, 0xfe, 0x7f, 
  0x00, 0x00, 0xfe, 0x7f, 0xc1, 0xfe, 0x7f, 0x00, 0x00, 0xfc, 0xff, 0x80, 0xff, 0x7e, 0x00, 0x00, 
  0xf9, 0xff, 0x7f, 0x7f, 0xbc, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 
  0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xce, 0x00, 0x00, 0x13, 0xff, 0xff, 0xff, 
  0xe7, 0x00, 0x00, 0x33, 0xff, 0xff, 0xff, 0xe7, 0x00, 0x00, 0x73, 0xff, 0xff, 0xff, 0xe7, 0x00, 
  0x00, 0x73, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x73, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x73, 
  0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x03, 0xff, 0xff, 
  0xff, 0xe0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 
  0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 
  0xff, 0xc7, 0x80, 0x00, 0x00, 0x00, 0x38, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x1f, 
  0xc0, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x1f, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x0f, 0x80, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad4 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x01, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x3b, 0xf8, 0x07, 
  0x00, 0x00, 0x00, 0x00, 0x37, 0xfc, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x2f, 0xff, 0xff, 0xc0, 0x00, 
  0x00, 0x00, 0x9f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 
  0x3f, 0xfe, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 
  0xff, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xcf, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xff, 0xcf, 0xf0, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0xcf, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xcf, 0xf0, 0x00, 0x00, 
  0x3f, 0xff, 0xff, 0xcf, 0xf0, 0x00, 0x00, 0x7f, 0xfe, 0xff, 0xef, 0xf0, 0x00, 0x00, 0x7f, 0xfc, 
  0x7f, 0xf7, 0xe0, 0x00, 0x00, 0x7f, 0xfb, 0x8f, 0xf8, 0x00, 0x00, 0x00, 0x7f, 0xc7, 0xc3, 0xfc, 
  0x00, 0x00, 0x00, 0x3f, 0x87, 0xf1, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x61, 0xf3, 0xff, 0xe0, 0x00, 
  0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x1f, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x04, 
  0xff, 0xfe, 0xff, 0xf0, 0x00, 0x00, 0x01, 0xff, 0xfc, 0xff, 0xf8, 0x00, 0x00, 0x09, 0xff, 0xf9, 
  0xff, 0xfc, 0x80, 0x00, 0x09, 0xff, 0xf9, 0xdf, 0xfc, 0x80, 0x00, 0x01, 0xff, 0xfc, 0x1f, 0xfc, 
  0x00, 0x00, 0x01, 0xff, 0xfe, 0x3f, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0xf8, 0x00, 0x00, 
  0x00, 0x7f, 0xff, 0x0f, 0xc0, 0x00, 0x00, 0x3e, 0x7f, 0xfe, 0xf7, 0x80, 0x00, 0x00, 0x3f, 0xe3, 
  0xff, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x03, 0xf8, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad5 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf7, 0xf4, 
  0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x73, 0xfc, 0x1f, 0x00, 0x00, 
  0x00, 0x00, 0xe7, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x7f, 0xc0, 0x00, 0x00, 0x00, 
  0x1f, 0xfe, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x8f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0xff, 
  0xff, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xdf, 0xf0, 
  0x00, 0x00, 0x0f, 0xff, 0xff, 0xcf, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xcf, 0xf0, 0x00, 0x00, 
  0x7f, 0xfc, 0x7f, 0xcf, 0xf0, 0x00, 0x00, 0x7f, 0xf8, 0x07, 0xcf, 0xf0, 0x00, 0x00, 0x7f, 0xf3, 
  0x83, 0xcf, 0xf0, 0x00, 0x00, 0x1f, 0xc7, 0xf1, 0xe7, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0xf3, 0xf0, 
  0x00, 0x00, 0x00, 0x00, 0x3f, 0xf3, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf3, 0xff, 0xf0, 0x00, 
  0x00, 0x00, 0x3f, 0xe0, 0xff, 0xf0, 0x00, 0x00, 0x04, 0xdf, 0xc8, 0xff, 0xf8, 0x00, 0x00, 0x0c, 
  0xe0, 0x13, 0xff, 0xfc, 0x00, 0x00, 0x0d, 0xf0, 0x33, 0xff, 0xfc, 0x00, 0x00, 0x0b, 0xff, 0xf3, 
  0xdf, 0xfc, 0x80, 0x00, 0x07, 0xff, 0xf8, 0x3f, 0xfc, 0x80, 0x00, 0x03, 0xff, 0xfc, 0x7f, 0xfc, 
  0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9f, 0xf8, 0x00, 0x00, 
  0x00, 0x7f, 0xff, 0x0f, 0xc0, 0x00, 0x00, 0x3e, 0x7f, 0xfe, 0xf7, 0x80, 0x00, 0x00, 0x3f, 0xe3, 
  0xff, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x03, 0xf8, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad6 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 
  0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfc, 
  0x00, 0x00, 0x00, 0x00, 0x3b, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfd, 0xfe, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 
  0x5f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x3f, 0xff, 0xff, 
  0x7f, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 
  0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 
  0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0x9f, 0xf8, 0x00, 0x00, 0x00, 0xff, 
  0xff, 0x9f, 0xf8, 0x00, 0x00, 0x00, 0x7e, 0x3f, 0xdf, 0xf8, 0x00, 0x00, 0x00, 0x3c, 0x1f, 0xcf, 
  0xf8, 0x00, 0x00, 0x00, 0x41, 0xe3, 0xcf, 0xf8, 0x00, 0x00, 0x00, 0xe3, 0xf7, 0xe7, 0xf0, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0xf3, 0xe0, 0x00, 0x00, 0x09, 0xff, 0xfd, 0xf8, 0x00, 0x00, 0x00, 0x09, 
  0xff, 0xf9, 0xfc, 0x00, 0x00, 0x00, 0x09, 0xff, 0xf3, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xe7, 
  0x7f, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xf0, 0xff, 0x80, 0x00, 0x00, 0x03, 0xff, 0xf9, 0xff, 0x00, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x38, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x18, 0x00, 0x00, 
  0x03, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7f, 
  0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x83, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, 0x00, 
  0x00, 0x00, 0x0f, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char sad_sad7 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 
  0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x1d, 
  0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0x7f, 
  0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x07, 0x80, 0x00, 0x00, 0x00, 0x37, 0xff, 0x8f, 0x80, 0x00, 
  0x00, 0x00, 0x63, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x9f, 0xe0, 0x00, 0x00, 0x1f, 
  0xff, 0xff, 0x9f, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, 
  0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf8, 
  0x00, 0x00, 0x03, 0xff, 0xff, 0xdf, 0xfc, 0x00, 0x00, 0x00, 0x7f, 0x07, 0xcf, 0xfc, 0x00, 0x00, 
  0x00, 0x3e, 0x03, 0xcf, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe7, 0xfc, 0x00, 0x00, 0x00, 0x01, 
  0xe0, 0xe7, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xc7, 0xf7, 0xfc, 0x00, 0x00, 0x00, 0x3c, 0x0f, 0xfb, 
  0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x00, 0x04, 0xc3, 0xfe, 0xfe, 0x00, 0x00, 
  0x00, 0x04, 0xff, 0xfd, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xfb, 0xff, 0xe0, 0x00, 0x00, 0x01, 
  0xff, 0xf3, 0x3f, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x7f, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xfc, 
  0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x90, 
  0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x90, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 
  0x00, 0x1f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x03, 
  0xff, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 
  0x80, 0x00, 0x00, 0x00, 0x07, 0x80, 0x07, 0x80, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0f, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 3328)
const unsigned char* sad_allArray[8] = {
  sad_sad0,
  sad_sad1,
  sad_sad2,
  sad_sad3,
  sad_sad4,
  sad_sad5,
  sad_sad6,
  sad_sad7
};


const unsigned char misc_cheer [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 
  0xf7, 0xbf, 0xf8, 0x00, 0x00, 0x00, 0x01, 0xf3, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x1f, 0xf2, 0xf9, 
  0x80, 0x00, 0x00, 0x00, 0x3f, 0xf1, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf0, 0x1e, 0x00, 0x00, 
  0x00, 0x00, 0xff, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x86, 0x00, 0x00, 0x03, 
  0xff, 0xdf, 0xff, 0xc6, 0x00, 0x00, 0x07, 0xdf, 0x8f, 0xbf, 0xe6, 0x00, 0x03, 0xff, 0xff, 0x0f, 
  0xc3, 0xf0, 0x00, 0x03, 0xff, 0xff, 0x1f, 0x81, 0xf0, 0x00, 0x03, 0xfc, 0xff, 0xff, 0x3c, 0xf8, 
  0x00, 0x03, 0xf0, 0xef, 0xfe, 0x04, 0xfc, 0x00, 0x00, 0x00, 0xe7, 0xf8, 0x0c, 0xfc, 0x00, 0x00, 
  0x00, 0xe7, 0xf1, 0xbc, 0xfc, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xc1, 0xfc, 0x00, 0x00, 0x00, 0xf8, 
  0x1f, 0xe3, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 
  0xfc, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3c, 0xff, 0xff, 0xff, 0xfd, 0xc0, 
  0x00, 0x38, 0xff, 0xff, 0xff, 0xfb, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xf7, 0xc0, 0x00, 0x00, 
  0x3f, 0xff, 0xff, 0xe7, 0x80, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 
  0xff, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xfe, 0x00, 
  0x00, 0x00, 0x00, 0x07, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const unsigned char misc_dead [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xe0, 
  0xff, 0xa0, 0x00, 0x00, 0x0e, 0xff, 0xfe, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0xe0, 
  0x00, 0x00, 0x01, 0xfe, 0xff, 0xcf, 0xf0, 0x00, 0x00, 0x19, 0xff, 0x03, 0xcf, 0xf8, 0x00, 0x00, 
  0x18, 0xff, 0x07, 0xe7, 0xf8, 0x00, 0x00, 0x10, 0xff, 0x3f, 0xf3, 0xf8, 0x00, 0x00, 0x19, 0xf8, 
  0x0f, 0xf3, 0xf8, 0x00, 0x00, 0x1f, 0xfc, 0x0f, 0xfb, 0xfc, 0x00, 0x00, 0x1f, 0xff, 0x3f, 0xf9, 
  0xfc, 0x00, 0x00, 0xc7, 0xff, 0x3f, 0xf9, 0xfc, 0x00, 0x00, 0xe7, 0xff, 0xff, 0xfc, 0xfc, 0x00, 
  0x01, 0xff, 0xff, 0xfc, 0xfe, 0x4c, 0x00, 0x03, 0xff, 0xff, 0xfc, 0xfe, 0x61, 0x80, 0x03, 0xff, 
  0xff, 0xfc, 0xff, 0x71, 0x80, 0x03, 0xff, 0xff, 0xfc, 0xff, 0x39, 0x80, 0x01, 0xff, 0xff, 0xfe, 
  0x7f, 0x30, 0x00, 0x00, 0x3f, 0xf3, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x1f, 0xe1, 0xf0, 0x7f, 0x00, 
  0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'bone', 24x24px
const unsigned char misc_bone [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x80, 0x00, 
  0x03, 0xc0, 0x00, 0x01, 0xf8, 0x00, 0x01, 0xfc, 0x00, 0x03, 0xfc, 0x00, 0x02, 0x1c, 0x00, 0x08, 
  0x00, 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x3f, 0xc0, 0x00, 0x3f, 0x80, 0x00, 0x7f, 0x80, 0x00, 
  0x0f, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'ball', 24x24px
const unsigned char misc_ball [] PROGMEM = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb6, 0xff, 0xff, 
  0x00, 0x7f, 0xfe, 0x31, 0x3f, 0xf9, 0xf3, 0x9f, 0xf3, 0x33, 0xcf, 0xe6, 0x11, 0xcf, 0xf4, 0xdc, 
  0xcf, 0xe4, 0xce, 0x2f, 0xe4, 0xcf, 0xef, 0xe4, 0x1c, 0xef, 0xf3, 0x38, 0x4f, 0xfb, 0xf3, 0x0f, 
  0xfd, 0xe7, 0x1f, 0xfc, 0x0e, 0x3f, 0xff, 0x00, 0x7f, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};



char *tasks[] = {"empty","empty","empty","empty","empty","empty","empty","empty","empty","empty"};
unsigned long duetime[10];
int weight[10];
int type[10]; // 1 is health, 2 is happiness

char *tasklist[] = {"Food", 
                    "Clean", 
                    "Break",
                    "Sleep", 
                    "Exercise", 
                    "60-223",
                    "67-262",
                    "15-213",
                    "05-392",
                    "Research"
                    };
int nextemptytask = 0;
int numtasks = 0;
int cursor = 0;
int cursor2 = 0;

const int butt1 = 4;
const int butt2 = 3;
const int butt3 = 2;
const int speaker = 5;
const int motor = 6;
int butt1state = 0;
int butt2state = 0;
int butt3state = 0;
int state = 0;

/* 
0 = idle
1 = input task
    (if they select sleep mode, it snoozes for 8 hours and doesn't draw anything)
2 = delete task
3 = set time for task
4 = sleep mode
5 = dead (press middle to reset)
6 = show tasks
*/

unsigned long idletime;
unsigned long frametime;
unsigned long buttontime;
unsigned long countdown;
unsigned long decrement;
bool deathcountdown;
// hour = 3600000
// minute = 60000
// this variable is changeable depending on how you want
// to increment things
unsigned long hour = 60000;
int frame = 0;
int happiness = 100;
int health = 100;
int animation = 0;
int tasksdone = 0;

void setup(void) {
  Serial.begin(9600);
  pinMode(butt1, INPUT);
  pinMode(butt2, INPUT);
  pinMode(butt3, INPUT);
  pinMode(motor, OUTPUT);
  digitalWrite(butt1, HIGH);
  digitalWrite(butt2, HIGH);
  digitalWrite(butt3, HIGH);
  state = 0;
  u8g.setFont(u8g_font_helvR08);
  idletime = millis();
  frametime = millis();
  buttontime = millis();
  countdown = 0;
  delay(1000);
  animation = (rand() % (3 + 1)) * 2; // sets idle animation on a random animation 
  for (int i = 0; i < 9; i++) {
    weight[i] = 0;
    type[i] = 0;
    duetime[i] = 0;
  }
}

void loop(void) {
  u8g.setFont(u8g_font_helvR08);
  butt1state = digitalRead(butt1);
  butt2state = digitalRead(butt2);
  butt3state = digitalRead(butt3);
  bool butt1press = false;
  bool butt2press = false;
  bool butt3press = false;
  // sets if the buttons have been pressed
  if (butt1state == LOW && millis() - buttontime >= 250) {
    buttontime = millis();
    butt1press = true;
    Serial.println("button 1 pressed");
  } else if (butt2state == LOW && millis() - buttontime >= 250) {
    buttontime = millis();
    butt2press = true;
    Serial.println("button 2 pressed");
  } else if (butt3state == LOW && millis() - buttontime >= 250) {
    buttontime = millis();
    butt3press = true;
    Serial.println("button 3 pressed");
  }

  
  // the buddy starts dying
  if (state != 5 && !deathcountdown && (happiness == 0 || health == 0)) {
    playSad();
    countdown = millis();
    deathcountdown = true;
  } 

  // this var can be changed depending on how long before something dies
  int deathcount = 10000
  if (deathcountdown && millis() - countdown >= deathcount) {
    state = 5;
    countdown = 0;
    deathcountdown = false;
  }

  if (health >= 100) health = 100;
  if (happiness >= 100) happiness = 100;
  if (happiness <= 0) happiness = 0;
  if (health <= 0) health = 0;
  
  // goes through the modes
  checkDelete();
  if (state == 0) {
    doIdle(butt1press, butt2press, butt3press);
  } else if (state == 1) {
    doInputTask(butt1press, butt2press, butt3press);
  } else if (state == 2) {
    doDeleteTask(butt1press, butt2press, butt3press);
  } else if (state == 3) {
    doSetTask(butt1press, butt2press, butt3press);
  } else if (state == 4) {
    doSleep(butt2press); // takes in the center button to wake up :D 
  } else if (state == 5) {
    doDead(butt2press); // takes in the center button to reset
  } else if (state == 6) {
    doShowTasks(butt2press); // takes in the center button to reset
  }
  //decrements happiness and health
  if (millis() - decrement >= 5000) {
    decrement = millis();
    happiness -= 1;
    health -= 1;
  }
}

// plays happy noises
void playCheer(void){
  // plays a little ditty 
  digitalWrite(motor, HIGH);
  tone(speaker, NOTE_D5, 200);
  delay(200);
  noTone(speaker);
  tone(speaker, NOTE_E5, 200);
  delay(200);
  noTone(speaker);
  tone(speaker, NOTE_F5, 200);
  delay(200);
  noTone(speaker);
  tone(speaker, NOTE_FS5, 1000);
  delay(500*1.3);
  noTone(speaker);
  digitalWrite(motor, LOW);
}

//plays sad noises
void playSad(void){
  digitalWrite(motor, HIGH);
  tone(speaker, NOTE_D5, 1000);
  delay(1000);
  digitalWrite(motor, LOW);
  noTone(speaker);
}


//shows all the tasks
void doShowTasks(int butt2press) {
  if (butt2press) {
    state = 0;
  }
  u8g.firstPage();
    do {
      drawDeleteTasks();
    } while(u8g.nextPage());
}

// dead state
void doDead(int butt2press) {
  // resets if butt2 is pressed
  if (butt2press) {
    state = 0;
    happiness = 90; 
    health = 90;
    tasksdone -= 1;
  }
  u8g.firstPage();
    do {
      u8g.drawStr( 0, 10, "Dead, dying, gone...");
      u8g.drawBitmapP( 56, 10, 7, 56, misc_dead);
    } while(u8g.nextPage());
}

// asleep state
void doSleep(int butt2press) {
  // wakes it up
  if (butt2press) {
    state = 0;
    countdown = 0;
    health -= 10; // from being woken up
    if (health > 100) {
      health = 100;
    }
    beSad();
  }
  // if it's 8 hours of sleep
  if (millis() - countdown == 28800000) {
    state = 0;
    countdown = 0;
    health += 15;
    if (health > 100) {
      health = 100;
    }
  }
  u8g.firstPage();
    do {
      u8g.drawStr( 0, 10, "Asleep...");
      u8g.drawBitmapP( 56, 10, 7, 56, health_sleep);
    } while(u8g.nextPage());
}

// checks through the list of tasks to see if any of them are over
void checkDelete(void) {
  for (int i = 0; i < numtasks; i++) {
    if (millis() >= duetime[i]) {
      Serial.println(tasks[i]);
      cursor = i;
      if (type[i] == 1) {
        health -= weight[i];
      } else if (type[i] == 2) {
        happiness -= weight[i];
      }
      deleteTask();
      beSad();
      tasksdone -= 1;
    }
  }
}

// deletes a task
void deleteTask(void) {
  tasks[cursor] = "empty";
  duetime[cursor] = 0;
  weight[cursor] = 0;
  type[cursor] = 0;
  for (int i = cursor; i < 9; i++) {
    tasks[i] = tasks[i + 1];
    duetime[i] = duetime[i + 1];
    weight[i] = weight[i + 1];
    type[i] = type[i + 1];
  }
  tasks[9] = "empty";
  duetime[9] = 0;
  weight[9] = 0;
  type[9] = 0;
  cursor = 0;
  cursor2 = 0;
  nextemptytask -= 1;
  tasksdone += 1;
  numtasks -= 1;
}

// deletes a task
void doDeleteTask(int butt1press, int butt2press, int butt3press) {
  if (nextemptytask == 0 && butt2press) {
    state = 0;
    u8g.firstPage();
    do {
      u8g.drawStr( 24, 10, "No tasks!");
    } while(u8g.nextPage());
  } 
  else {
  u8g.firstPage();
    do {
      drawDeleteTasks();
    } while(u8g.nextPage());
  }
  
  if (nextemptytask != 0) {
    if (butt1press && cursor < numtasks - 1) {
      Serial.println("Pressed");
      cursor += 1;
    } 
    else if (butt3press && cursor > 0) {
      Serial.println("Pressed");
      cursor -= 1;
    }
    else if (butt2press) {
      if (type[cursor] == 1) {
        health += weight[cursor];
        // bonus for finishing an hour early
        if (millis() - duetime[cursor] > hour) {
          health += weight[cursor];
        }
        if (health >= 100) {
          health = 100;
        }
      } else if (type[cursor] == 2) {
        happiness += weight[cursor];
        // hour bonus 
        if (millis() - duetime[cursor] > hour) {
          health += weight[cursor];
        }
        if (happiness >= 100) {
          happiness = 100;
        }
      }
      deleteTask();
      state = 0; // reverts to idle display
      u8g.firstPage();
      do {
        u8g.drawBitmapP( 32, 10, 7, 56, misc_cheer);
      } while(u8g.nextPage());
      playCheer();
    }
    
    }
}

// Draws all the tasks to delete
void drawDeleteTasks(void) {
  u8g.drawStr( 0, 10, "List of tasks:");
  for (int i = 0; i < 5; i++) {
    u8g.drawStr(10, i * 10 + 24, tasks[i]);
    char time[10];
    if (duetime[i] != 0) {
      itoa((duetime[i] - millis()) / 1000,time,10);
      u8g.drawStr(50, i * 10 + 24, time); 
    }
  }
  for (int i = 5; i < 10; i++) {
    u8g.drawStr(70, (i - 5) * 10 + 24, tasks[i]);
    char time[10];
    
    if (duetime[i] != 0) {
      itoa((duetime[i] - millis()) / 1000,time,10);
      u8g.drawStr(110, (i - 5) * 10 + 24, time); 
    }
  }
  if (cursor < 5) {
    u8g.drawCircle(5, cursor * 10 + 20, 1);
  } else if (cursor >= 5) {
    u8g.drawCircle(55, (cursor-5) * 10 + 20, 1);
  }
}

// sets a time for a task
void doSetTask(int butt1press, int butt2press, int butt3press) {
  if (butt2press) {
    unsigned long duet = millis() + (cursor2 * hour);
    Serial.println(duet);
    tasks[nextemptytask] = tasklist[cursor];
    switch(cursor) {
      case 0: // food
        type[nextemptytask] = 1;
        weight[nextemptytask] = 10;
        duetime[nextemptytask] = duet;
        break;
      case 1: // clean
        type[nextemptytask] = 1;
        weight[nextemptytask] = 5;
        duetime[nextemptytask] = duet;
        break;
      case 2: // break
        type[nextemptytask] = 1;
        weight[nextemptytask] = 5;
        duetime[nextemptytask] = duet;
        break;
      case 3: // sleep 
        type[nextemptytask] = 1;
        weight[nextemptytask] = 15;
        duetime[nextemptytask] = duet;
        break;
      case 4: // exercise
        type[nextemptytask] = 1;
        weight[nextemptytask] = 5;
        duetime[nextemptytask] = duet;
        break;
      case 5: // 60-223
        type[nextemptytask] = 2;
        weight[nextemptytask] = 10;
        duetime[nextemptytask] = duet;
        break;
      case 6:
        type[nextemptytask] = 2;
        weight[nextemptytask] = 10;
        duetime[nextemptytask] = duet;
        break;
      case 7:
        type[nextemptytask] = 2;
        weight[nextemptytask] = 15;
        duetime[nextemptytask] = duet;
        break;
      case 8:
        type[nextemptytask] = 2;
        weight[nextemptytask] = 10;
        duetime[nextemptytask] = duet;
        break;
      case 9:
        type[nextemptytask] = 2;
        weight[nextemptytask] = 10;
        duetime[nextemptytask] = duet;
        break;    
      default :
         Serial.println("invalid cursor setting\n");
    }
    cursor = 0;
    cursor2 = 0;
    nextemptytask += 1;
    numtasks += 1;
    happiness += 5;
    state = 0; // reverts to idle display
  } else if (butt1press && cursor2 < 24) {
    cursor2 += 1;
  } else if (butt3press && cursor2 >= 1) {
    cursor2 -= 1;
  } 
  u8g.firstPage();
    do {
      drawSetTasks();
    } while(u8g.nextPage());
}

// a timer for how long to do it
// draws timing for tasks
void drawSetTasks(void) {
  u8g.drawStr(10, 10, "Done by?");
  char curse[7];
  itoa(cursor2,curse,10);
  u8g.drawStr(56, 32, curse);
  u8g.drawStr(56, 50, "minutes");
}

// inputs a new task
void doInputTask(int butt1press, int butt2press, int butt3press) {
  if (butt2press) {
    state = 3; 
    // if sleep was selected
    if (cursor == 3) {
      state = 4;
      cursor = 0; 
      cursor2 = 0;
    }
    cursor2 = 1;
  } else if (butt1press && cursor != 9) {
    cursor += 1;
  } else if (butt3press && cursor != 0) {
    cursor -= 1;
  } 
  u8g.firstPage();
    do {
      drawInputScreen();
    } while(u8g.nextPage());
}

// draws the input screen for adding a new task
void drawInputScreen(void) {
  u8g.drawStr( 0, 10, "Add a new task to your list!");
  for (int i = 0; i < 5; i++) {
    u8g.drawStr(16, i * 10 + 24, tasklist[i]);
  }
  for (int i = 5; i < 10; i++) {
    u8g.drawStr(64, (i - 5) * 10 + 24, tasklist[i]);
  }
  if (cursor < 5) {
    u8g.drawCircle(10, cursor * 10 + 20, 1);
  } else if (cursor >= 5) {
    u8g.drawCircle(56, (cursor-5) * 10 + 20, 1);
  }
  
}

// default for being sad after having missed a task
void beSad(void) {
  u8g.firstPage();
    do {
      u8g.drawBitmapP( 56, 10, 7, 56, sad_allArray[4]);
    } while(u8g.nextPage());
  playSad();
}

// idle screen
void doIdle(bool butt1press, bool butt2press, bool butt3press) {
  if (butt1press) {
    state = 1;
    Serial.println(state);
  } 
  else if (butt2press) {
    state = 6;
  } 
  else if (butt3press) {
    state = 2;
    Serial.println(state);
  } else if (millis() - idletime >= 5000) {
    idletime = millis();
    animation = (rand() % (3 + 1)) * 2;
  } else if (millis() - frametime >= 500) {
    frame += 1;
    if (frame == 2) frame = 0;
    frametime = millis();
    u8g.firstPage();
    do {
      drawIdle(animation, frame);
    } while(u8g.nextPage());
  }
}

// finds how close the next upcoming task is in milliseconds
unsigned long nextTask(void) {
  if (numtasks == 0) {
    return 0;
  }
  int leastnum = 0;
  unsigned long least = duetime[0] - millis();
  for (int i = 0; i < numtasks; i++) {
    if (duetime[i] - millis() < least) {
      least = duetime[i] - millis();
      leastnum = i;
    }
  }
  return leastnum;
}

// draws the idle screen
void drawIdle(int animation, int frame) {
  u8g.drawStr( 0, 10, "Happiness:  ");
  char happy[7];
  itoa(happiness, happy, 10);
  u8g.drawStr(54, 10, happy);  
  u8g.drawStr( 0, 25, "Health:  ");
  char hea[7];
  itoa(health,hea,10);
  u8g.drawStr(35, 25, hea);
  u8g.drawStr(0, 40, "Next Task:");
  unsigned long displayTime = 0;
  if (numtasks != 0) {
    int leastnum = nextTask();
    displayTime = duetime[leastnum] - millis();
    displayTime = displayTime / 1000;
    char distime[10];
    itoa(displayTime,distime,10);
    u8g.drawStr(0, 50, tasks[leastnum]);
    u8g.drawStr(40, 50, distime);
  } 
  else if (numtasks == 0) { 
    char distime[10];
    itoa(displayTime,distime,10);
    u8g.drawStr(0, 50, distime);
  }
    
  if (happiness > 50 && health >50 ) {
    u8g.drawBitmapP( 56, 10, 7, 56, happy_allArray[animation + frame]);
  }
  else if (happiness <= 50 || health < 50) {
    u8g.drawBitmapP( 56, 10, 7, 56, sad_allArray[animation + frame]);
  }
  if (tasksdone >= 5) {
    u8g.drawBitmapP(38, 40, 3, 24, misc_bone);
  }
  
}

Pitches File Used:

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define END -1

 

]]>
Dice Lightning https://courses.ideate.cmu.edu/60-223/f2021/work/dice-lightning/ Mon, 01 Nov 2021 22:57:08 +0000 https://courses.ideate.cmu.edu/60-223/f2021/work/?p=14263  

Final completed project

 

Highlighted Part

Here is the keypad which I soldered and screwed onto the wood board

 

Item in Action

Pushing the 5 button to set the number of kicks

 

Pushing the * button to initiate kicking

 

Here is the first kick with the dice in the air

 

Decisions that affected Output

The first big decision that changed the outcome significantly was the change from IR remote and IR sensor for wireless input to the metal keypad. This was because I felt like the keypad would be more fun to use and since this device is going to e used really close by to me anyways so there really isn’t any need for the wireless device.

The second big decision was the removal of the wooden prongs that would hold the top dice platform in place when it got kicked up. As I was putting the wooden pieces in they weren’t placed perfectly and created friction with the wooden platform preventing it from kicking up as hard as it could. It was also making the glueing of the device really difficult since I had put them in before glueing the whole device together.

Here is what it looked like with the wooden rods in

This is the attachment of the keypad to the rest of the circuit with the big rainbow cable

This is the first prototype of the project before any external components were added

 

Discussion

I think some additional stability for the upper platform would help a lot for actually flipping dice so that it does not tilt, as well as some more height with the plastic cover so the dice can bounce/flip.” I agree about the additional stability for the platform but the way I was implementing was not a viable option because the order that I did things in, but the plastic cover doesn’t need any more height added to it since the solenoids don’t kick it high enough to ever hit the ceiling anyways.

“Doesn’t seem to actually flip the dice consistently, but that could be fixed with a little more iteration.” This is true as the solenoids struggle with the weight of the wooden platform and the dice on it, this could be solved by adding more solenoids to simply kick the platform up with more force.

I am extremely proud and happy with the project, for the most part it does everything I want it to and it looks really nice. I would have liked for it to only need one cable connecting it instead of two on the back since having two makes it a little annoying to use. I would also make the top platform out of a lighter wood like balsa without having to use a laser cutter. Other than that though the project works exactly as intended and is really fun to use and look at.

I really enjoyed using the laser cutters. Working with the wood and acrylic and putting the pieces together was a lot of fun and I think I’ll definitely be doing it again in the future. If I could go back I would tell myself to start with the box and then put the circuitry inside the box and go from there instead of the other way around since I just shoved all of my circuits in the box haphazardly instead of putting everything in a nice spot to make it look good.

If I were to build another iteration I would for sure add more solenoids to make it kick harder since that was the main issue that I was having. I would also add some sort of voltage transformer to the circuit to power the Arduino inside so that I don’t have to have two cables coming out of the back of the project to make it easier to use.

 

Schematic and Block Diagram

Block diagram

 

Circuit schematic

Code

/*
 * Dice Lightning
 * 
 * Team: Ronald Gonzalez
 * 
 * Description:
 *   The code takes turns turning on the input pins
 *   of the keypad and then depending on where it 
 *   read an output it knows what the key pressed
 *   was.
 *   
 *   Then it will call the solenoid function which
 *   based on the number in keypadVal will turn off
 *   and on the solenoid so that it starts kicking
 *   the dice around.
 * 
 *     Arduino Pin | Description
 *     ------------|---------------------
 *     2           | SolenoidOn
 *     3           | KeypadIn1       
 *     5           | KeypadIn2   
 *     6           | KeypadIn3      
 *     8           | KeypadOut1     
 *     9           | KeypadOut2   
 *     10          | KeypadOut3
 *     11          | KeypadOut4
 *   
 */

// Solenoid Control
#define SOLENOIDON 2

// Keypad Input Signals
#define KEYPADIN1 3
#define KEYPADIN2 5
#define KEYPADIN3 6

// Keypad Output Signals
#define KEYPADOUT1 8
#define KEYPADOUT2 9
#define KEYPADOUT3 10
#define KEYPADOUT4 11

// Delay for Button Pushes
#define DELAY 250

// Keeps track of number being pressed
unsigned int keypadVal = 0;

// Tells the solenoid to start firing
bool start = false;


void setup() {
  // Keypad setup
  pinMode(KEYPADIN1, OUTPUT);
  pinMode(KEYPADIN2, OUTPUT);
  pinMode(KEYPADIN3, OUTPUT);
  pinMode(KEYPADOUT1, INPUT);
  pinMode(KEYPADOUT2, INPUT);
  pinMode(KEYPADOUT3, INPUT);
  pinMode(KEYPADOUT4, INPUT);

  // Solenoid setup
  pinMode(SOLENOIDON, OUTPUT);
}

//Makes the solenoid kick keypadVal number of times
void solenoid(){
  
  start = false; //reset start so that it doesn't keep firing
  
  if(keypadVal == 0) return;
  
  for(int i = 0; i < keypadVal; i++) {
    digitalWrite(SOLENOIDON, HIGH);
    delay(2 * DELAY);
    digitalWrite(SOLENOIDON, LOW);
    delay(3 * DELAY);
  }
  
  keypadVal = 0; //reset keypadVal
}

// Reads from the keys on the keypad and changes keypadVal
// or start depending on the button pressed
void key(int row){
  int keys[4];
  int sig; //signal being sent to key

  /* 
   * Based on the row of the keypad that we are 
   * working with the keys and where we send the
   * signal to change
   *
   */
  if(row == 1) {
    int keys[] = {11, 1, 4, 7};
    sig = KEYPADIN3;
  }
  else if(row == 2) {
    int keys[] = {0, 2, 5, 8};
    sig = KEYPADIN1;
  }
  else if(row == 3) {
    int keys[] = {12, 3, 6, 9};
    sig = KEYPADIN2;
  }

  // Send a signal to the keypad
  digitalWrite(sig, HIGH);

  // Read from all keypadOuts to see what key is being pressed
  if(digitalRead(KEYPADOUT1) == HIGH) {
    if(row == 1) start = true; // * key, also the start
    else if(row == 2) keypadVal = 0; // # key, also the reset
    else if(row == 3) keypadVal * 10; // 0 key
    delay(DELAY);
  }
  if(digitalRead(KEYPADOUT2) == HIGH) {
    keypadVal = keypadVal * 10 + keys[1];
    delay(DELAY); 
  }
  if(digitalRead(KEYPADOUT3) == HIGH) {
    keypadVal = keypadVal * 10 + keys[2];
    delay(DELAY);
  }
  if(digitalRead(KEYPADOUT4) == HIGH) {
    keypadVal = keypadVal * 10 + keys[3];
    delay(DELAY);
  }

  // Turn signal back off
  digitalWrite(sig, LOW);

}

// Send all signals to the keypad in order
void loop() {
  
  for(int i = 1; i <= 3; i++) key(i);

  // If the start button has been pressed the solenoid will fire
  if(start) solenoid();
}

 

]]>