Final documentation – 60-223 Work https://courses.ideate.cmu.edu/60-223/s2018/work Intro to Physical Computing: Student Work Tue, 18 Sep 2018 21:43:43 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.25 Book Reminder by Dog-Loving Duo: Final Documentation https://courses.ideate.cmu.edu/60-223/s2018/work/book-reminder-by-dog-loving-duo-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/book-reminder-by-dog-loving-duo-final-documentation/#respond Sat, 12 May 2018 02:03:16 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3683 Introduction:

We are students at Carnegie Mellon University taking a class in physical computing, a project-based course in which we learn the basic skills in programming and electronics to build simple interactive devices.  Throughout this course and each project, we have been challenged to raise the bar and use our newfound knowledge to create fun, interactive, and creative devices.  Our current and final project challenges us to make a device that would benefit not ourselves but someone else.  We were assigned to meet with Joseph, an older gentlemen from the Pittsburgh community, and to create a device that would somehow benefit his life.  The device doesn’t need to be anything grand or hold commercial value,  it is simply specifically designed to help out Joseph by solving a troublesome aspect of his life.  Our group, the Dog-Loving Duo, consisting of Kristie and Logan, first met with Joseph at his apartment in downtown Pittsburgh on Friday, March 30 to learn about his life and decide on what to build.  From there, we had a little less than two weeks to build a prototype design to present to Joseph to get his feedback.  After that, we had over three weeks to create our final project, which will be given to Joseph.  This post is the documentation of last three weeks of work and describes how we arrived at our final product from our prototype.

If you want more information on our initial meeting with Joseph, click here

If you want more information on our initial prototype, click here

Group photo of our group, the Dog-Loving Duo with Joseph and our device

What we built:

Our device is designed to help you keep track of where you last left off in a book.  Each book you are reading will have a special bookmark we created inside of it.  When you open the book to read, you take the bookmark out and move it over the top of our box and a short recording will play from whatever speakers or headphones you attach to the box.  This recording will be a previously recorded summary of what last happened in the book.  After you are done reading, you click and hold the record button for however long you wish to record a new summary of what happened.  This new summary will be recorded over your previous one.  After that, you place the bookmark back inside the book until the next time you start reading.  In short, our device will play back a recording of what last happened in the book you are reading, so as to refresh your memory and help you keep track of what’s happening in multiple books.

Overall View of Book Reminder along with the 10 Bookmarks

Close Up of Individual Bookmark placed on top of the Box

Close Up of Jacks: the jack closest to you in the photo is the one for power and the jack further away is the one that is connected to the speaker seen in the photo

Look into the Hidden Electronic of the box that can’t normally be seen when left side is attached

Close Up of Microphone on top of Box

Close up of Record Button on front of Box

Using the Book Reminder:

Place bookmark on top of the box

Listen to previous recorded summary

Press and hold button as well as speak into mic on top of the box for new recording

Before his three monthly book club meetings, Joseph needs to read three different books.  He picks up the first book he needs to read, “The Great Gatsby,” and opens up to where he last left off.  He takes out the bookmark inside the book, places it over top of the Book Reminder, and listens to his previous recorded summary of what happened.  After reading for a few hours, he places the bookmark back inside the book and prepares to record a new summary.  He holds down the record button on the front of the box and speaks into the microphone on top of the box describing the events that just occurred.  He describes how Tom is cheating on Daisy with a married women as well as the great metaphors he read.

A few days later, Joseph decides to continue reading the book for his other book club, “Pride and Prejudice.”  Just like before, he opens up to where he last left off and listens to his previous recorded summary.  After reading for awhile, he places the bookmark back inside the book and records a new summary.  Thankfully, the Book Reminder keeps track of all the different books Joseph is reading and helps remind him of what last happened in his three different books.

How we got here:

From this previous prototype:

Initial prototype design

to our new and a lot nicer final project:

Final Book Reminder

After our previous prototype, we had a lot of work to do.  First instead of using an Arduino, we decided to switch to using a Teensy, a more advanced electronic that is better suited for doing audio recording.  Therefore, we did some research into using a Teensy for audio recording.  After looking through multiple websites, we found one that was very helpful that discussed using a Teensy and an Audio Shield for recording.  I also met with an IDeATe professor named Jesse, who is skilled in doing audio recording, to ask for his recommendation for a microphone.  Therefore, we needed to order a bunch of new parts, so we went to Amazon and ordered: a Teensy 3.5, the audio shield recommended by the website, a 3.5 mm jack to be used as a plug in for speakers, and a MEMS microphone that was recommended by Jesse.  As we were waiting for our new parts, we decided to plan our design for the box as you can see in the photo below.

Initial Design for Final Box

After our new parts arrived, we needed to do a lot of soldering.  First, we needed to solder the Teensy with male pins and the Audio Shield with female pins.  After that, we also needed to solder the RFID tag reader and the MEMS mic.

Soldering Teensy

Soldering Audio Shield

After, everything was soldered, we needed to test each part individually to make sure they all worked.  The first part we wanted to test was the RFID tag reader since this part gave us the most trouble in our previous prototype.  Therefore, we used a code from the library of the RC522 RFID tag reader and wired it to an Arduino to make sure it worked (see photo below).  Unfortunately, this took time because the library was not great so we asked one of our friends for their code that worked when they previously used the same RFID tag reader.  After editing their code, we finally got it working.

Testing RFID reader with Arduino

Next, we needed to test if the RFID tag reader could still read tags through wood, so we could be sure it could work inside our wooden box.

Testing to make sure the RFID tag reader can read tags through plywood

After it successfully read tags through wood multiple times, we needed to make sure the RFID tag reader was compatible with the teensy (see photo below).

Testing RFID tag reader with Teensy

After successfully getting the RFID tag reader to work with the Teensy, we needed to make sure the audio shield worked, which it did.  The next step was to check if everything worked together, so we wrote the code for everything and wired it all (see photo below).

Testing Audio Shield, Microphone, and RFID Reader all together

Unfortunately, the audio shield and RFID tag reader weren’t working together since they required that you use the same pins for MISO, MOSI, and SCK.  Therefore, to fix this problem, we need to solder wires coming from the audio shield at those pins.

Soldered three wires on audio shield that needed to be used with the RFID reader

Finally, after fixing this. we could test everything together.  We also decided to test out a new microphone to see if that worked better than the MEMS mic (see photo).  Like expected, this microphone didn’t work better, so we continued using the MEMS microphone.

Testing new microphone to see if it works better

Next, we needed to lasercut a box to place our electronics in.  This box was to be made according to our initial design with a hole for the button in the front, two holes for the jacks on the side, and a hole on the top so the microphone recorded better.  To make the box, we used a website called MakerCase and then SolidWorks to draw the holes into our box design. Next, we needed to mount the button and jacks onto the box.

Mounting button to front of box

After mounting everything, we needed to check again that everything worked and then put our box together.  First, we taped the microphone and RFID tag reader to the top of the box.  Next, we taped the Teensy to the button of the box and started rewiring everything and gluing the box together.

Testing everything while the jacks and button are mounted to the lasercut box

Putting the box together: taped RFID tag reader and mic to top of box

Putting the box together.  First, connected the bottom and right side together.

Overall, we followed our initial design for our final project although we didn’t quite follow our design plan since we ran into a lot more difficulties than expected, so we finished a lot later than expected (1 am the night before it was due).

Conclusions and lessons learned:

Much of the feedback we received was positive, which was really nice to hear.  Joseph and his wife were especially happy with our project.  The two standout constructive criticisms were that the audio quality was a large barrier to practical use, and that some aspects of the physical design could be improved to make interacting with it more intuitive.  The design feedback mainly revolved around the device being too minimal and not enough visual queues for things like if the box registered a tap, or how to know where the microphone is.  Improving the way we record/playback audio would be the biggest thing I’d improve before bringing this project to any kind of product scale.  Another huge critique we received was on the design of the box.  A few people would have preferred it to be smaller and Joseph himself requested if we could make it look more like a book, which if given more time would be one quick thing to fix.

A large part of working with an older person as a client was realizing that we really didn’t have to change much of the design process to make something useful.  As much as some people might have different problems from each other, learning to work and design things to solve problems is a universal skill.  One big surprise for us was how tech savvy Joseph was.  We really didn’t need to explain much for him to get our ideas or the purpose of the project, which was great and a huge plus when planning the design.  I was also surprised by how easy it was to talk to and work with Joseph since I was initially nervous due to the huge age gap.

A big takeaway from this course has been simple things take a lot of work to get right.  This project is a great example of that because it has a very simple (in our opinion) interaction, but it took a lot of testing and tweaking to get it working nicely.  Some challenges from the software side were mainly getting the hardware modules to work nicely with each other.  Translating the RFID tags to strings to store the audio files was more challenging that we initially thought.  Specifically translating the byte representation to a string took a while to debug because of some quarks of how the Arduino deals with strings/char arrays.  Also making the SD writer and audio shield play nicely with the RFID reader was an additional source of challenge as they communicate over I2C so we had to switch around a few default pins so they’d all end up on the same bus.

Technical details:

Better formatted code here

 

/******************************************************************************************
* *
* Intro to Physical Computing: Final Project *
* Bookmark Box *
* *
* Logan Kojiro -- Kristie Lord *
* ****************************************************************************************
* Parts of this are modified from a MFRC522 library example; see *
* https://github.com/miguelbalboa/rfid for further details and other examples. *
* *
* Audio Recording is modified from example code with the teensy audio library *
* See Examples->Audio->Recorder *
* *
* Released into the public domain. *
* ****************************************************************************************
*/

/************ LIBRARIES ************/
#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>
#include <Audio.h>
#include <SerialFlash.h>
#include <Bounce.h>

/************ SETUP FOR RECORDING ************/
AudioInputI2S i2s2;
AudioAnalyzePeak peak1;
AudioRecordQueue queue1;
AudioPlaySdRaw playRaw1;
AudioOutputI2S i2s1;
AudioConnection patchCord1(i2s2, 0, queue1, 0);
AudioConnection patchCord2(i2s2, 0, peak1, 0);
AudioConnection patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1;

/************ GLOBALS & PIN ASSIGNMENTS ************/
#define RST_PIN 37
#define SS_PIN 38
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14
const int myInput = AUDIO_INPUT_MIC;
const int BUTTON = 32;
File frec; //file where data is recorded
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
Bounce buttonRecord = Bounce(BUTTON, 8);
char *curr_tag = NULL;
bool recording = false;

/******************************************************************************************
* Setup: *
* ****************************************************************************************
*/
void setup() {
pinMode(BUTTON, INPUT_PULLUP);

AudioMemory(60);
sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);

Serial.begin(9600);
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
SPI.begin();
Serial.println("Initializing Card...");
mfrc522.PCD_Init(); // Init MFRC522 card
mfrc522.PCD_DumpVersionToSerial(); //show details of card reader module
Serial.println("Done.");

Serial.println("Initializing SD Card...");
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here if no SD card, but print a message
while (1) {
Serial.println("Unable to access the SD card");
delay(1000);
}
}
Serial.println("Done.");
Serial.println("\n\n--Begin Main--\n\n");
}

/******************************************************************************************
* Main Loop: *
* ****************************************************************************************
*/
void loop() {
buttonRecord.update();

if (buttonRecord.fallingEdge()) {
Serial.println(curr_tag);
record();
}

else {
if (mfrc522.PICC_IsNewCardPresent()) {
free(curr_tag);
Serial.println("Found Card!");

//make sure we can read the card
if ( !mfrc522.PICC_ReadCardSerial()) return;

//construct the filename from the card's UID
String file = "";

for (byte i = 0; i < mfrc522.uid.size - 1; i++) {
file += String(mfrc522.uid.uidByte[i]);
}
file += ".RAW";

//some arguments for SD functions must be of type (char *)
//not String so we must convert the String to a Char*
char *buf = (char*)malloc(sizeof(char) * (file.length() + 1));
file.toCharArray(buf, file.length() + 1);
curr_tag = buf;

Serial.print("Tag Filename: ");
Serial.println(curr_tag);
play_msg(curr_tag);
}
mfrc522.PICC_HaltA();
}
}

/******************************************************************************************
* record: while button is held, record from mic *
* and write to SD card *
* ****************************************************************************************
*/
void record() {
if (curr_tag == NULL) {
Serial.println("ERROR: No Card has been tapped");
return;
}
Serial.print("Recording to file ");
Serial.print(curr_tag);
Serial.println("...");
startRecording(curr_tag);
if (!recording) {
Serial.println("ERROR: Unable to write to SD card");
return;
}
while (!digitalRead(BUTTON)) {
continueRecording();
}
stopRecording(curr_tag);
}

/******************************************************************************************
* play_msg: open sound file from SD card and playback *
* *
* ****************************************************************************************
*/
void play_msg(char *filename) {
Serial.print("Playing Message from file ");
Serial.print(filename);
Serial.println("...");
if (!SD.exists(filename)) {
Serial.println("No recording for this tag yet");
return;
}
playRaw1.play(filename);
while (playRaw1.isPlaying()) {
}
playRaw1.stop();
Serial.println("\nDone.");
}

/******************************************************************************************
* startRecording: open SD file and begin the recording queue *
* *
* ****************************************************************************************
*/
void startRecording(char *filename) {
Serial.println("Recording...");
if (SD.exists(filename)) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove(filename);
}
frec = SD.open(filename, FILE_WRITE);
Serial.println(filename);
if (frec) {
queue1.begin();
recording = true;
}
}

/******************************************************************************************
* continueRecording: Fetch 2 blocks from the audio library and copy *
* into a 512 byte buffer. The Arduino SD library is most efficient *
* when full 512 byte sector size writes are used. *
* ****************************************************************************************
*/
void continueRecording() {
if (queue1.available() >= 2) {
byte buffer[512];
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer + 256, queue1.readBuffer(), 256);
queue1.freeBuffer();
// write all 512 bytes to the SD card
frec.write(buffer, 512);
}
}

/******************************************************************************************
* stopRecording: stop recording to the queue, save additional data left *
* on the queue, and close the SD file *
* *
* ****************************************************************************************
*/
void stopRecording(char *filename) {
queue1.end();
if (recording) {
//write the rest of the audio buffer to the file and close it
while (queue1.available() > 0) {
frec.write((byte*)queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.close();
}
recording = false;
Serial.print("\nRecorded to ");
Serial.println(filename);
}

[\code]

 

Schematic

Website used to make box: http://www.makercase.com/

DXF file of box:

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/book-reminder-by-dog-loving-duo-final-documentation/feed/ 0
Freezer Inventory by Team Wilma: final documentation https://courses.ideate.cmu.edu/60-223/s2018/work/freezer-inventory-by-team-wilma-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/freezer-inventory-by-team-wilma-final-documentation/#respond Sat, 12 May 2018 01:33:15 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3528 Introduction:

Our goal with this project was to create an assistive device to help improve a part of someone’s day-to-day life. We teamed up with Wilma, a local elderly woman, and met with her to develop a product that addresses a personal problem that she has. During our initial meeting, she noted how she has difficulty remembering the contents of her basement freezer. We quickly identified this as a problem that we could address.  Because she finds it difficult to go downstairs, Wilma wanted a way to know exactly what was in her downstairs freezer without having to walk down from the main floor to check. We set out to develop a way for Wilma to keep track of her freezer inventory from the main floor. To see details on our first iteration, refer to our documentation here.

What we built:

Our final product allows for Wilma to easily track the inventory of her freezer. As she uses or buys new food items, she can log the changes in quantity using a device nearby her downstairs freezer. Wilma  can do this by selecting a card from a stack that includes all of her most frequently used food items, and tapping the card against the device. The display then shows the food and its current quantity, which can be updated with two buttons (plus/minus). As Wilma updates her inventory from the downstairs freezer, the device on the first floor instantly updates to reflect the changes. While Wilma is upstairs, she can scroll through a list showing what foods she has and how many she has of them, which saves her a trip downstairs before she goes on a shopping run.

Overall Photo:

Overall Photo of Device : Scanner box to be put in the basement (Left), and display box to be put on the first floor (Right)

Detail Photos:

Close-up of scanner box – The front plate is transparent to see where the scanner is

Close up of display box – Number of each food item will be updated accordingly to the basement scanner box input

The two boxes in the basement and first floor are connected with an Ethernet cable

Different cards are available for scanning

In Use Photos:

A food item card must be scanned to update its count

Wilma can then press the plus or minus button to update the count of the food

Wilma can easily keep track of her groceries with the RFID cards

The updated count will then be reflected on the first floor display box, and the user can rotate the switch to scroll through the display for different food items

Wilma can scroll through her freezer inventory on the first floor

Narrative:

Wilma is on the main floor of her house when she realizes she needs to go grocery shopping. She does not remember what she bought a couple weeks  earlier or what her and her husband have been eating recently. Instead of walking downstairs to check her freezer, she goes to the display next to the door and scrolls through the list of her freezer inventory. Now that she has a good idea of what she needs to buy, Wilma is able to go grocery shopping to stock up on carrots, potatoes, and beef.

When Wilma comes back from the store, she goes downstairs to put her groceries in the freezer. After Wilma places the beef in the freezer, she taps the “beef” card on our gadget, and adds one to the beef count. Later that night, Wilma is looking through her freezer inventory upstairs, trying to decide what to make for dinner. Now that her freezer is fully stocked with beef, carrots, and potatoes, she decides to make beef stew for her and her husband.

How we got here:

We went through many iterations of this product before it took on its final appearance. Originally,  this product started out as a physical representation of a shopping list. Downstairs, nearby the freezer, we made many blocks that could be placed on a board. On the upstairs display, the items on the downstairs board were reflected on a small LCD display, to serve as a reminder before Wilma decides to go shopping.

Our prototype using food blocks

Even before we arrived at this design, we went through several iterations to perfect the block detecting mechanism. The main idea is that by adding the block to the board,  the block would complete a voltage divider circuit. Using this circuit, we could detect which block is on the board. One of the big challenges we faced initially was to get good electrical contact between the blocks and the board.

This is how we initially tried to detect the block using conductive tape.

Although the shopping board design had its strengths (we got feedback from Wilma and others that they really liked the tactile element of our project), we decided that we needed to revamp this project in order to make it truly practical. One issue that was noted by several people was that the blocks were too bulky. This made it impractical to make many blocks, because it would take up too much physical space. Another problem with our initial design was that it could only display three food items at a time. For our final design, we wanted to make a product that was more flexible than this. Overall, people seemed to like our solution to address Wilma’s accessibility problem. Wilma herself noted that it would be useful to know what is in her freezer downstairs without having to actually go downstairs.

For our final product, we wanted to draw from the strengths of our original design (physical element and downstairs/upstairs convenience), while addressing some of the shortcomings (bulkiness, limited number of food items). The solution we came up for the bulkiness issue, that also made the device more reliable overall), was to swap the blocks for RFID cards. The RFID cards, roughly the size and thickness of a credit card, are stored neatly in a holder nearby the downstairs device. To address the limited number of food items, we implemented a scrolling feature that takes advantage of a rotary encoder.  We found that this was an intuitive way to search through large lists of food.

Image of brainstorming process for our initial iteration of the final design

A picture to show when we successfully set up the scanner and the display with the RFID cards

Picture to show how the food items were displayed on the display box

Even to get to this point, however, we ran into many technical issues. One of the larger issues that we had was how to get the two arduinos to communicate with one another. We started by simply connecting the arduinos via the tx and rx pins and trying to send over a simple string. From there we tried to get the string to become a usable variable. Once we were able to get that done the issue was how to send over the information we wanted, two different numbers and allow the receiving arduino to know which number corresponded to which variable. We were able to get it to work by adding each incoming character as a string and then using a colon as an identifier to differentiate the two different variables. Then we converted the separated strings to integers which we were then able to use to update the spot in the arrays for the food and counts.

Another aspect of our project that we changed from our initial iteration is the method for communicating between the two arduinos. We initially wanted to have them communicate wirelessly using bluetooth. However, we realized that this may be problematic if the arduinos are placed on separate floors, and communication becomes unreliable. The next option we came up with was an Ethernet cable, which would allow reliable communication. After visiting Wilma’s house, we were able to confirm that this was a viable option. There was already a telephone cable running from the first floor to the basement, so adding another cable would not be an issue. Moreover, there were outlets near the basement freezer, which allows easy installation of the device.

Outlet near the freezer – allows easy installation of our device

Telephone cord running from freezer (seen in bottom left corner of picture) to upstairs through the staircase

Gap between wall and stairs so cables can run through

Conclusions:

The final crit was very valuable in giving us ideas of how we could improve our project in the future if we were to move forward as well as allowing us to see what we did well. I think people liked the overall functionality of our project. The ability to have the two separate systems talk to each other and have the different displays update together seemed to be a useful idea that we implemented well. There were a couple ideas brought up during the final crit that seemed to make sense for future iterations of the product. Several people commented on different ways to display the content on the upstairs device. One person made the fair point that it would be useful to “be able to sort (the) display by number.”. We also noticed that it would be more helpful for Wilma if the display box is more interactive, such as it acting as a shopping list and reminding her to buy a certain food item.

We found that working with an older person was an interesting process. In the initial stage when trying to identify a problem, it was difficult as they have already accustomed to a certain lifestyle, and were wary of introducing technology since they did not grow up with it. However, we appreciated how cooperative Wilma was, as she came to our meeting with ideas in her mind about certain issues technology could solve. Also, we realized that showcasing our past projects was very effective for introducing new ideas. If we have another opportunity to work with an older person, we would want to have more examples of how technology can help someone’s life, to provide a better understanding of physical computing. Visiting Wilma’s house was also a major step in the process, as it immediately provided us with a more realistic sense of how she may use this device and how it can be implemented in her house.

In terms of the iteration process, we recognized the importance of exploring different options when hitting obstacles, instead of trying to pursue the same method for a long time period. Especially with the issue for communication between the two arduinos, we were able to find the best method by fast paced trial and error. When working under a time pressure, we found that it was important to keep open perspectives and explore other viable options.

Overall, we really enjoyed working with Wilma, and are happy with how our final project turned out. Hopefully, Wilma will find our device useful in keeping track of the foods she needs to buy.

Circuit Diagram:

Technical Details:

Code for Basement:

/**************************************************************************/
/*!
 @file readMifareClassic.pde
 @author Adafruit Industries
 @license BSD (see license.txt)

This example will wait for any ISO14443A card or tag, and
 depending on the size of the UID will attempt to read from it.

If the card has a 4-byte UID it is probably a Mifare
 Classic card, and the following steps are taken:

Reads the 4 byte (32 bit) ID of a MiFare Classic card.
 Since the classic cards have only 32 bit identifiers you can stick
 them in a single variable and use that to compare card ID's as a
 number. This doesn't work for ultralight cards that have longer 7
 byte IDs!

Note that you need the baud rate to be 115200 because we need to
 print out the data and read from the card at the same time!

This is an example sketch for the Adafruit PN532 NFC/RFID breakout boards
 This library works with the Adafruit NFC breakout
 ----> https://www.adafruit.com/products/364

Check out the links above for our tutorials and wiring diagrams
 These chips use SPI to communicate, 4 required to interface

Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!
*/
/**************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
#include <LiquidCrystal_I2C.h>
#include <Math.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#ifdef __AVR__
#include <avr/power.h>
#endif

// If using the breakout or shield with I2C, define just the pins connected
// to the IRQ and reset lines. Use the values below (2, 3) for the shield!
#define PN532_IRQ (2)
#define PN532_RESET (3) // Not connected by default on the NFC Shield

// Uncomment just _one_ line below depending on how your breakout or shield
// is connected to the Arduino:

// Use this line for a breakout with a hardware SPI connection. Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins. On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12. The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);

// Or use this line for a breakout or shield with an I2C connection:
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
// also change #define in Adafruit_PN532.cpp library file
#define Serial SerialUSB
#endif
const int POSBUT = 8;
const int NEGBUT = 9;
int lastButP = 0;
int ButP = 0;
int lastButN = 0;
int ButN = 0;
int indexNum = 0;

char* foods[] {
 "Beef",
 "Lamb",
 "Pork",
 "Venison",
 "Salmon",
 "Tuna",
 "Cod",
 "Trout",
 "Shrimp",
 "Clams",
 "Strawberries",
 "Cherries",
 "Mixed fruits",
};
int counts[sizeof(foods) / 2] = {0};
const unsigned long idnums[sizeof(foods) / 2] = {
 1288439685,
 1828249131,
 969280133,
 2838093867,
 1903429509,
 1624895643,
 4041343644,
 1345447067,
 1343218843,
 1079115932,
 3491956869,
 756091013,
 1961469925,
};
uint32_t cardid = 0;
uint32_t lastcardid = 0;

void setup(void) {
 pinMode(POSBUT, INPUT);
 pinMode(NEGBUT, INPUT);
 Serial.begin(115200);
#ifndef ESP8266
 while (!Serial); // for Leonardo/Micro/Zero
#endif

nfc.begin();

uint32_t versiondata = nfc.getFirmwareVersion();
 if (! versiondata) {
   Serial.print("Didn't find PN53x board");
   while (1); // halt
 }
 // configure board to read RFID tags
 nfc.SAMConfig();
 // Initialize lcd
 lcd.init();
 lcd.backlight();

}


void loop(void) {
 //pin setup for buttons
 ButP = digitalRead(POSBUT);
 ButN = digitalRead(NEGBUT);
 uint8_t success;
 uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
 uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)

// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
 // 'uid' will be populated with the UID, and uidLength will indicate
 // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
 success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 20);
 if (success) {
    cardid = uid[0];
    cardid <<= 8;
    cardid |= uid[1];
    cardid <<= 8;
    cardid |= uid[2];
    cardid <<= 8;
    cardid |= uid[3];
    //new card detected!
    if (cardid != lastcardid) {
       for (int index = 0; index < sizeof(foods) / 2; index = index + 1) {
          if (idnums[index] == cardid) {
             indexNum = index;
          }
    }
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(0, 1);
    lcd.print(" ");
   }
 }
 //check if button was pressed, and update count
 if (ButP == 1) {
    if (ButP != lastButP) {
       counts[indexNum] = counts[indexNum] + 1;
       Serial.print(String(indexNum));
       Serial.print(":");
       Serial.println(String(counts[indexNum]));
       lcd.setCursor(0, 1);
       lcd.print(" ");
    }
    delay(25);
 }
 else if (ButN == 1) {
    if (ButN != lastButN) {
       counts[indexNum] = counts[indexNum] - 1;
       if (counts[indexNum] < 0) {
          counts[indexNum] = 0;
       }
       Serial.print(String(indexNum));
       Serial.print(":");
       Serial.println(String(counts[indexNum]));
       lcd.setCursor(0, 1);
       lcd.print(" ");
    }
    delay(25);
 }
 lastButP = ButP;
 lastButN = ButN;
 lastcardid = cardid;

//display on lcd
 if (cardid != 0) {
    lcd.setCursor(0, 0);
    lcd.print(foods[indexNum]);
    lcd.setCursor(0, 1);
    lcd.print("Count:" + String(counts[indexNum]));
 }
}

Code for First Floor:
#include <Adafruit_PN532.h>

// Setting up the counter
int reading = 0;
int lowest = -24;
int highest = 0;
int changeamnt = 1;
int array[] = {1, 2, 3, 4, 5, 6, 7, 8};
// Timing for polling the encoder
unsigned long currentTime;
unsigned long lastTime;
int lcdreading = 0;

// Pin definitions
const int pinA = 2;
const int pinB = 3;
int change = 0;
// Storing the readings

boolean encA;
boolean encB;
boolean lastA = false;
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Math.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
char* foods[] {
 "beef",
 "lamb",
 "pork",
 "venison",
 "salmon",
 "tuna",
 "cod",
 "trout",
 "shrimp",
 "clams",
 "strawberries",
 "cherries",
 "oranges",
 "apples",
 "mixed fruits"
};
int counts[sizeof(foods) / 2] = {0};
int index = 0;
const unsigned long idnums[sizeof(foods) / 2] = {
 1288439685,
 1828249131,
 969280133,
 2838093867,
 1903429509,
 1624895643,
 4041343644,
 1345447067,
 1343218843,
 1079115932,
 3491956869,
 756091013,
 1961469925
};
boolean stringComplete = false;
String inputString = "";
char rcvd;
String inString = "";
int a = 0;
int b = 0;
void setup() {
 // set the two pins as inputs with internal pullups
 pinMode(pinA, INPUT_PULLUP);
 pinMode(pinB, INPUT_PULLUP);
 // Set up the timing of the polling
 currentTime = millis();
 lastTime = currentTime;
 // Start the serial monitor for debugging
 lcd.init();
 lcd.backlight();
 Serial.begin(115200);
}


void loop()
{
 if (Serial.available() > 0) {
    int inChar = Serial.read();
    inString += (char)inChar;
    int lineIndex = inString.indexOf('\n');
    int colonIndex = inString.indexOf(':');
    if (inChar == '\n') {
       String indexNumS = inString.substring(0, colonIndex);
       String countNumS = inString.substring(colonIndex + 1, lineIndex);
       a = indexNumS.toInt();
       b = countNumS.toInt();
       counts[a] = b;
       Serial.println(a);
       Serial.println(b);
       inString = "";
    }
 }
 currentTime = millis();

if (currentTime >= (lastTime + 5))
 {
 // read the two pins
 encA = digitalRead(pinA);
 encB = digitalRead(pinB);
 // check if A has gone from high to low
    if ((!encA) && (lastA))
    {
    // check if B is high
       if (encB)
       {
       // clockwise
       if (reading + changeamnt <= highest)
          {
          reading = reading + changeamnt;
          change = 1;
          lcd.setCursor(0, 0);
          lcd.print(" ");
          lcd.setCursor(0, 1);
          lcd.print(" ");
          lcd.setCursor(0, 2);
          lcd.print(" ");
          lcd.setCursor(0, 3);
          lcd.print(" ");
       }
    }
 else
 {
 // anti-clockwise
    if (reading - changeamnt >= lowest)
    {
       reading = reading - changeamnt;
       change = -1;
       lcd.setCursor(0, 0);
       lcd.print(" ");
       lcd.setCursor(0, 1);
       lcd.print(" ");
       lcd.setCursor(0, 2);
       lcd.print(" ");
       lcd.setCursor(0, 3);
       lcd.print(" ");
    }
 }
 // Output reading for debugging
 Serial.print("Change ");
 Serial.println(change);
 Serial.println(reading);
 }
 // store reading of A and millis for next loop
 lastA = encA;
 lastTime = currentTime;
 lcdreading = (-1) * reading;

}
 if (lcdreading > 25 ) {
    lcdreading = 0;
 }
 Serial.print("lcdreading ");
 Serial.println(lcdreading);
 lcd.setCursor(0, 0);
 //lcd.print(" ");
 lcd.print(foods[lcdreading]);
 lcd.print(counts[lcdreading]);
 lcd.setCursor(0, 1);
 //lcd.print(" ");
 lcd.print(foods[lcdreading + 1]);
 lcd.print(counts[lcdreading + 1]);
 lcd.setCursor(0, 2);
 //lcd.print(" ");
 lcd.print(foods[lcdreading + 2]);
 lcd.print(counts[lcdreading + 2]);
 lcd.setCursor(0, 3);
 //lcd.print(" ");
 lcd.print(foods[lcdreading + 3]);
 lcd.print(counts[lcdreading + 3]);
}
]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/freezer-inventory-by-team-wilma-final-documentation/feed/ 0
Puzzle Box by Red Chameleon: Final Documentation https://courses.ideate.cmu.edu/60-223/s2018/work/puzzle-box-by-red-chameleon-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/puzzle-box-by-red-chameleon-final-documentation/#respond Fri, 11 May 2018 19:42:35 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3604 Team Members: Ryan, Willow, Chileshe

INTRODUCTION

The purpose of this project was to create an assistive device for Phil Sidel, an elderly individual in our community. Phil lives a very simple life that consists of waking up to read emails in the morning, walking to the Jewish Community Center to shower and have lunch, come back home to edit monthly newsletters for the Explorers Club, make peanut butter jelly sandwiches or soup for dinner, and then solve sudoku puzzles until it is time to sleep. Since his day-to-day schedule consists of basic tasks that he doesn’t need much help in completing, we thought the best product to make for him would be a puzzle because its what he loves to do in his free time. Chileshe came up with the initial idea of having three hidden operations and having the user input the four numbers in order to reach a final goal number. Phil loved the idea of this and took time in analyzing how he would solve a problem like this, so we decided to go along with his idea into its final form. We’ve described below the process we took to create this box.

To see our documentation for our initial meeting: https://courses.ideate.cmu.edu/60-223/s2018/work/meeting-documentation/
To see our documentation for our initial prototype: https://courses.ideate.cmu.edu/60-223/s2018/work/team-red-chameleon-prototype-documentation/

WHAT WE BUILT

The puzzle box has four dials, one on each of its sides, where each dial represents a number from 1-9 and can be controlled by the user. There are three operations (addition, subtraction, or multiplication) that occurs between each of the numbers. The mathematical result from the four numbers that are chosen by the user through the dials and the three hidden operations chosen by the Arduino is shown through the display on the top of the box as “Curr” for Current Number. It also displays a “Goal” which is the number that the user is trying to reach through changing the four numbers. If the user successfully reaches the goal number, then it displays “You’ve Won!” on the display and gives you a new puzzle. This puzzle box was inspired by Phil’s passions for puzzles, specifically the KenKen puzzle, which consists of giving operations and the user having to fill in the numbers in order to reach a desired number. This puzzle box is the complete reverse where we choose the operations and the user has to decide on the numbers, so we thought it would be new and creative for Phil to be able to solve a different type of math puzzle.

Overall View of the Puzzle Box

Front View – Details of the Number Knob, On/off Switch and Charging Port

Top View – Details of the Game Controls and Displays

Craft Details – Sanded Acrylic Panels Softening LED Lights

Craft Details – Floating Number Knobs Ensuring Smoothness

HOW WE GOT HERE

Our puzzle box for the prototype was fully functional, so the process in making the final product of the box was not very different, with just some improvements. We’ve improved it by having solder connections instead of having loose female-male wire and breadboard connections. We’ve also improved the number knob to display the numbers more clearly and by also showing the range for each of the numbers. Although these improve seem simple and small, these changes caused many issues with the hardware as explained in further details below.

  • Using Solder Connections

A big improvement we made with this project from the prototype was by soldering all of our connections that are inside of the board so that no wires can come loose. Instead of using breadboards, we used the thin breadboards, where we can solder in the connections directly into the thin breadboard, for all connections to 5V and Ground.

The solder connections for the top face.

Breaking thin breadboards into smaller sizes to fit inside of the box.

A look into the box for how the thin breadboard/solder connections for the potentiometer all come together.

  • Number Knob Improvement

Based on feedback from Philip, we improved the design of the number knobs on four sides of the puzzle box. We scaled up the numbers so that Philip can read the input more easily. We also added the range lines, and made the number hand pointing to a number range instead of a specific number, this way Philip doesn’t need to make the number hand perfectly align with the actual number display when he wants to choose that number. In addition, we doubled the thickness of the number hand, so that it doesn’t easily break.

Installing the knobs with the improved dials

  • Rechargeable Battery

The rechargeable battery was a new hardware for all of us, so it was difficult to figure out how to use at first. However, it was definitely a huge improvement from the first prototype because the prototype ran off a 9V battery and would have to be replaced by opening up the box, which would’ve been impossible to do with all of the wiring. Also a power cable would’ve been difficult to use because you need to rotate the box as you’re using it, so a power cable would’ve inhibited the user’s movements. Therefore, the rechargeable battery allows the user to interact with the box without being limited by a cable while also being able to charge it through a cable while not using it.

  • Assembling using Super Glue

The prototype of the puzzle box was glued together using hot glue, which was thought was sufficient to keep the box together. However, we found that with some pressure, each of the faces could come out, so we used super glue to assemble the knobs and the box together. Therefore, the box would be more durable and would not break as easily.

Assembling the box using super glue.

  • Potentiometer Malfunctioning

The biggest problem we encountered during the assembly process was with the potentiometers. After wiring and gluing all potentiometers in place, we found that they are not working properly: the numbers are jumping all over the place. We checked every single place that could go wrong, and changed the four potentiometers for so many times, however the problem still persisted. Then we noticed that if we press the legs of the potentiometers, sometimes they would turn back to their normal state. We realized that the problem might come from the connection area. We decided to calm down and thought hard about any differences between our final model and our previous perfectly functioning prototype. We eventually figured out: the wires were soldered into the holes above the potentiometer legs instead of the legs themselves, and that somehow damaged the potentiometers. This was a big lesson for us: we should’ve done testing for smaller parts before assembly everything together.

Trying to debug the potentiometer issue with the solder

  • LCD Display Dimming Problem

During the assembly process, our LCD display was somehow really dim. Initially we thought this was a wiring problem, but we did not find anything wrong after careful checking. We then used the voltage meter and found out that the LCD was not receiving enough power. Later we found that the problem comes from the change of the power source. The LCD needs to be directly powered from the re-chargable battery instead of from the Arduino board like what we had before.

Fixing the dim LCD problem before final assembly.

  • Level Button Connection Mistake

After we assemble all three level buttons in place, they are not working. We checked the wiring and found that one of the buttons was soldered on the wrong side of the legs. Again, we probably should’ve test the button one by one before gluing everything into the wood.

Despite all of these issues, we were able to complete the fully functional puzzle box after 15 hours of work put into the physical creation of it. Most of the time went into fixing the potentiometers not working when soldered, but it was well worth the effort in the end. Phil loved it and we can not wait to hear responses on his experiences with it.

Final testing of the box after assembly.

 

CONCLUSIONS & LESSONS

In our final critique, we received positive feedback for the most part and varying responses on whether the puzzle was solvable for them or just impossible for them to solve. Individuals seemed to enjoy playing with our device and thought it was an intriguing idea to make a project like ours. They suspected that an algorithm may be achieved to solve the puzzle more easily, but this did not take away from enjoyment of interacting with the device. We generally received positive feedback. One individual suggested a set of instructions to go along with the device, although we did not think this was necessary because this box was meant for Phil and the instructions would be explained to him in person. Something we learned through the critique was that Phil had misunderstood our initial explanation of the box, making it seem like the puzzle was awfully harder than it actually was. However, Phil was able to understand the puzzle fully through this critique and found the puzzle to be definitely solvable, which was good feedback to hear.

We’ve learned a few things in this project, with communication as one of the most significant areas. Communication of ideas must be done very patiently, and with a lot of tact. We noticed that there was often a misunderstanding when we explained the working of the device to the elderly. What seemed intuitive to us, was easily misinterpreted by others despite its seeming simplicity. For instance, there’s a number representing current value of the operations on the device, along with the symbol “CURR” indicating this, but a few individuals needed more explanation to understand that this was the case. We experienced a similar issue, once or twice, with the number labeled as the “GOAL”. Intuitions differ between individuals, and people often jump to different assumptions, even for simple mechanisms. Since this was inspired by the KenKen puzzle, one that Phil loves to solve, Phil kept thinking that the rules of KenKen were what we were trying to go for and kept asking us what the operations were. However, after he understood that the operations was what was hidden and was supposed to be solved for, he started to understand the puzzle much better from there.

A very significant lesson, as mentioned earlier, is the value of testing. We experienced several unexpected issues relating to soldering or potentiometers in our project, which stopped our final device construction for many hours. Testing every single piece after any sort of change, no matter how minor, can save a lot of time. We focused on testing the initial wiring of the device, so that we could write code for it. However, we should have continued testing each piece throughout the final construction process. We were extremely confident after the prototyping phase, so we did not consider that issues might emerge in our final design, especially because the changes we made after prototyping were extremely minor. Ironically, the time we assumed would be wasted on extra testing, would have saved us several times that duration on constructing the final device.

TECHNICAL DETAILS

      • Schematic

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

//I2C pins declaration
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

//The Pins being used for this project
int pot1Pin = A0;
int pot2Pin = A1;
int pot3Pin = A2;
int pot4Pin = A3;
int ledPinLeft = 2;
int ledPinRight = 3;
int submitPin = 5;
int resetPin = 6;
int level1Pin = 8;
int level2Pin = 9;
int level3Pin = 10;

//initialize global variables
int level = 1;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int val4 = 0;
int num1 = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
int op1 = 0;
int op2 = 0;
int op3 = 0;
int sol = 0;
int direc = 0;
int start = 0;
int ans = 0;
int press_time = 0;
int last_time = 0;
int press_reset_time = 0;
int last_reset_time = 0;

void setup() {
  lcd.begin(16, 2); //Defining 16 columns and 2 rows of lcd display
  lcd.backlight(); //To Power ON the back light

  //Initialize the modes of each pin
  pinMode(pot1Pin, INPUT);
  pinMode(pot2Pin, INPUT);
  pinMode(pot3Pin, INPUT);
  pinMode(pot4Pin, INPUT);
  pinMode(ledPinLeft, OUTPUT);
  pinMode(ledPinRight, OUTPUT);
  //buttons are input_pullup
  pinMode(submitPin, INPUT_PULLUP);
  pinMode(resetPin, INPUT_PULLUP);
  pinMode(level1Pin, INPUT_PULLUP);
  pinMode(level2Pin, INPUT_PULLUP);
  pinMode(level3Pin, INPUT_PULLUP);

  randomSeed(analogRead(A5)); //for the random number generator

  //initialize first puzzle that will be given once started up
  direc = random(0, 2);
  start = random(0, 4);
  op1 = random(0, 3);
  op2 = random(0, 3);
  op3 = random(0, 3);
  num1 = random(1, 10);
  num2 = random(1, 10);
  num3 = random(1, 10);
  num4 = random(1, 10);
  sol = generateSolandDirec(num1, num2, num3, num4, op1, op2, op3);

  Serial.begin(9600);
}


int generateSolution(int num1, int num2, int num3, int num4, int op1, int op2, int op3) {
  /*This function takes in four numbers and the operations between them, and returns what
    the output number is. It follows order of operations */
  if (((op1 == 2) && (op2 == 2)) && (op3 == 2)) {
    return num1 * num2 * num3 * num4;
  }
  else if ((op1 == 2) && (op2 == 2)) {

    if (op3 == 1) {
      return num1 * num2 * num3 - num4;
    }
    return num1 * num2 * num3 + num4;
  }
  else if ((op2 == 2) && (op3 == 2)) {
    if (op1 == 1) {
      return num1 - num2 * num3 * num4;
    }
    return num1 + num2 * num3 * num4;
  }
  else if ((op1 == 2) && (op3 == 2)) {
    if (op2 == 1) {
      return num1 * num2 - num3 * num4;
    }
    return num1 * num2 + num3 * num4;
  }
  else if (op1 == 2) {
    if ((op2 == 0) && (op3 == 0)) {
      return num1 * num2 + num3 + num4;
    }
    else if ((op2 == 1) && (op3 == 0)) {
      return num1 * num2 - num3 + num4;
    }
    if ((op2 == 0) && (op3 == 1)) {
      return num1 * num2 + num3 - num4;
    }
    if ((op2 == 1) && (op3 == 1)) {
      return num1 * num2 - num3 - num3;
    }
  }
  else if (op2 == 2) {
    if ((op1 == 0) && (op3 == 0)) {
      return num1 + num2 * num3 + num4;
    }
    else if ((op1 == 1) && (op3 == 0)) {
      return num1 - num2 * num3 + num4;
    }
    if ((op1 == 0) && (op3 == 1)) {
      return num1 + num2 * num3 - num4;
    }
    if ((op1 == 1) && (op3 == 1)) {
      return num1 - num2 * num3 - num4;
    }
  }

  else if (op3 == 2) {
    if ((op1 == 0) && (op2 == 0)) {
      return num1 + num2 + num3 * num4;
    }
    else if ((op1 == 0) && (op2 == 1)) {
      return num1 + num2 - num3 * num4;
    }
    if ((op1 == 1) && (op2 == 0)) {
      return num1 - num2 + num3 * num4;
    }
    if ((op1 == 1) && (op2 == 1)) {
      return num1 - num2 - num3 * num4;
    }
  }


  if (((op1 == 0) && (op2 == 0)) && (op3 == 0)) {
    return num1 + num2 + num3 + num4;
  }
  else if (((op1 == 0) && (op2 == 0)) && (op3 == 1)) {
    return num1 + num2 + num3 - num4;
  }
  else if (((op1 == 0) && (op2 == 1)) && (op3 == 1)) {
    return num1 + num2 - num3 - num4;
  }
  else if (((op1 == 1) && (op2 == 1)) && (op3 == 1)) {
    return num1 - num2 - num3 - num4;
  }
  else if (((op1 == 1) && (op2 == 1)) && (op3 == 0)) {
    return num1 - num2 - num3 + num4;
  }
  else if (((op1 == 1) && (op2 == 0)) && (op3 == 0)) {
    return num1 - num2 + num3 + num4;
  }
  else if (((op1 == 1) && (op2 == 0)) && (op3 == 1)) {
    return num1 - num2 + num3 - num4;
  }
  else if (((op1 == 0) && (op2 == 1)) && (op3 == 0)) {
    return num1 + num2 - num3 + num4;
  }

}

int getValue(int val) {
  //given the value of the potentiometers, return what number the dial should be
  if (val < 19) {
    return 1;
  }
  else if (val < 160) {
    return 2;
  }
  else if (val < 300) {
    return 3;
  }
  else if (val < 460) {
    return 4;
  }
  else if (val < 590) {
    return 5;
  }
  else if (val < 720) {
    return 6;
  }
  else if (val < 855) {
    return 7;
  }
  else if (val < 1010) {
    return 8;
  }
  else {
    return 9;
  }
}


int generateSolandDirec(int num1, int num2, int num3, int num4, int op1, int op2, int op3) {
  //given the direction and starting point, call the generateSolution function with the desired order
  if (direc == 0) { //going right
    if (start == 0) {
      return generateSolution(num1, num2, num3, num4, op1, op2, op3);
    }
    else if (start == 1) {
      return generateSolution(num2, num3, num4, num1, op1, op2, op3);
    }
    else if (start == 2) {
      return generateSolution(num3, num4, num1, num2, op1, op2, op3);
    }
    else {
      return generateSolution(num4, num1, num2, num3, op1, op2, op3);
    }
  }
  else { // going left
    if (start == 0) {
      return generateSolution(num1, num4, num3, num2, op1, op2, op3);
    }
    else if (start == 1) {
      return generateSolution(num2, num1, num4, num3, op1, op2, op3);
    }
    else if (start == 2) {
      return generateSolution(num3, num2, num1, num4, op1, op2, op3);
    }
    else {
      return generateSolution(num4, num3, num2, num1, op1, op2, op3);
    }
  }
}

void loop() {

  //turn the direction LED's on according to the direction the puzzle is going
  if (direc == 0) {
    digitalWrite(ledPinLeft, HIGH);
    digitalWrite(ledPinRight, LOW);
  }
  else {
    digitalWrite(ledPinLeft, LOW);
    digitalWrite(ledPinRight, HIGH);
  }

  //read the values from the potentiometer
  int pot1Read = analogRead(pot1Pin);
  int pot2Read = analogRead(pot2Pin);
  int pot3Read = analogRead(pot3Pin);
  int pot4Read = analogRead(pot4Pin);

  //determine what level is pressed (initially at 1 if no button is pressed)
  int level1Read = digitalRead(level1Pin);
  int level2Read = digitalRead(level2Pin);
  int level3Read = digitalRead(level3Pin);
  if (!level1Read or !level2Read or !level3Read) {
    if (!level1Read) {
      Serial.print("Easy");
      level = 1;
    }
    else if (!level2Read) {
      Serial.print("Medium");
      level = 2;
    }
    else {
      Serial.print("Hard");
      level = 3;
    }
  }

  //check if the submit or new game buttons are pressed
  int submit = digitalRead(submitPin);
  int reset = digitalRead(resetPin);
  if (!submit) { //if the submit button is pressed
    press_time = millis();
    if (press_time - last_time >= 3000) { //if 3 seconds have passed since last press
      Serial.print("Submit");

      //get the value from each dial
      int val1 = getValue(pot1Read);
      int val2 = getValue(pot2Read);
      int val3 = getValue(pot3Read);
      int val4 = getValue(pot4Read);
      Serial.print(val1);
      Serial.print(val2);
      Serial.print(val3);
      Serial.print(val4);
      Serial.print("\n");

      //calculate what the current number is at
      ans = generateSolandDirec(val1, val2, val3, val4, op1, op2, op3);
      if (ans == sol) { //check if the guessed answer is equal to the goal
        //display "You've Won!" if the answer is correct
        lcd.setCursor(0, 0);
        lcd.print("YOU'VE WON!");
        delay(5000);

        //generate a new puzzle
        op1 = random(0, 3);
        op2 = random(0, 3);
        op3 = random(0, 3);
        num1 = random(1, 10);
        num2 = random(1, 10);
        num3 = random(1, 10);
        num4 = random(1, 10);
        direc = random(0, 2);
        start = random(0, 3);
        sol = generateSolandDirec(num1, num2, num3, num4, op1, op2, op3);
      }
      last_time = press_time;
    }
  }

  if (!reset) { //if reset button is pressed
    press_reset_time = millis();
    if (press_reset_time - last_reset_time >= 1000) { //ensure button has not been pressed twice within a second
      Serial.print("Reset");

      //generate a new puzzle
      op1 = random(0, 3);
      op2 = random(0, 3);
      op3 = random(0, 3);
      num1 = random(1, 10);
      num2 = random(1, 10);
      num3 = random(1, 10);
      num4 = random(1, 10);
      direc = random(0, 2);
      start = random(0, 3);
      sol = generateSolandDirec(num1, num2, num3, num4, op1, op2, op3);
      last_reset_time = press_reset_time;
    }
  }

  //display the goal number on the LCD
  lcd.setCursor(0, 0);
  lcd.print("GOAL: ");
  lcd.print(sol);

  //display the OPS on the LCD
  lcd.setCursor(13, 0);
  lcd.print("OPS");

  //display the current number on the LCD
  lcd.setCursor(0, 1);
  lcd.print("CURR: ");
  lcd.print(ans);

  if (level == 1) { //if easy level, display two operations
    lcd.setCursor(13,1);
    if (op1 == 0) {
      lcd.print("+");
    }
    else if (op1 == 1) {
      lcd.print("-");
    }
    else {
      lcd.print("x");
    }
    lcd.setCursor(15,1);
    if (op2 == 0) {
      lcd.print("+");
    }
    else if (op2 == 1) {
      lcd.print("-");
    }
    else {
      lcd.print("x");
    }
  }
  else if (level == 2) { //if medium level, display one operation
    lcd.setCursor(14,1);
    if (op1 == 0) {
      lcd.print("+");
    }
    else if (op1 == 1) {
      lcd.print("-");
    }
    else {
      lcd.print("x");
    }
  }
  delay(200);

  lcd.clear();//Clean the screen

  //if information is wanted about the values from potentiometer and the solution numbers uncomment these lines
//    Serial.print(getValue(pot1Read));
//    Serial.print(getValue(pot2Read));
//    Serial.print(getValue(pot3Read));
//    Serial.println(getValue(pot4Read));
  
//    Serial.print(val1);
//    Serial.print(val2);
//    Serial.print(val3);
//    Serial.print(val4);
//    Serial.print(" ");
//    Serial.print(num1);
//    Serial.print(num2);
//    Serial.print(num3);
//    Serial.print(num4);
//    Serial.print(" ");
//    Serial.print(op1);
//    Serial.print(op2);
//    Serial.print(op3);
//    Serial.print(" Direction: ");
//    Serial.print(direc);
//    Serial.print(" Start: ");
//    Serial.print(start);
//    Serial.print(" Answer: ");
//    Serial.print(ans);
//    Serial.print(" Solution: ");
//    Serial.println(sol);
}
      • DXF file for the box design:

 

 

 

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/puzzle-box-by-red-chameleon-final-documentation/feed/ 0
Fischer-randomizer by Team Steve: Final Documentation https://courses.ideate.cmu.edu/60-223/s2018/work/team-steve-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/team-steve-final-documentation/#respond Fri, 11 May 2018 04:26:05 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3525 Fischer-random Chess: automated randomizer
Built by: Josh Lefevre, Nathan Serafin, & Alyssa Casamento

A brief overview

In the Spring semester of 2018, our team of Carnegie Mellon students was paired with a gentleman named Steve, to develop a device that would make an element of his daily life convenient and entertaining in some way. At our first design research and interview with Steve we had acknowledged his interest in the game of chess. We began to explore effective ways in which we could improve his quality of playing as an avid player and teacher. After numerous iterations of ideas, we decided to create an automated Fischer-random chess device. This device would help Steve increase the speed of initializing chess tournament games, and help him to teach Fischer Random chess to others.

In our 1st publicized iteration, we recorded our process and critique session with Steve, and others. The feedback we received from these events solidified our overall direction of the project and encouraged us to improve our initial design. We began creating our final product, with a more legible, compact and user friendly design. This post is a discussion of our process and design decisions that led to our final product for Steve.

What we built

What it does

End view of the chess randomizer. The total thickness is about 1.25 inches

Our project is a device that facilitates the initial set-up of the back row (king, queen, bishops, knights, and rooks) of a chess game in accordance with the rules of the Fischer-random variant. The device’s interface is simple to use. One presses a power switch on the device and then pushes  the “randomize” button that chooses one of the 960 possible setup combinations of Fischer-random chess. A single standard chess icon lights up with a unique color near each backrow square to indicate where the piece should be placed on the chess board. A total of eight pieces are chosen during each randomization process.

Pressing the randomizer button initiates the algorithm to randomize the back row.

Pressing the power button & the green lights turn on to indicate boot up has occurred.

 

 

 

 

 

Pressing and holding the button only gives one pattern

Detail of the charging port

We used a combination of LED strips, etched/frosted acrylic and laser cut icons to diffuse and create the illumination of symbols seen.

Detail shot of the illuminated pieces. The vertically etched lines show separation between spaces on a chess board. We deliberately did not code them black or white because this setup allows use on either side of the chess board.

 

 

 

 

 

 

GIF of  Basic internal functional components

during  use:(light diffusers, random button, Arduino and LEDs)

Intended use

When Steve is looking to play a round of Fischer random chess, he can pull out this device and align it with the chessboard, using the notch. Once he has placed the device on the table, he can use the power button to turn the device on and then press the “randomize” button to display a new legal, randomized set up for Fischer-random. Then Steve can quickly set up his back row and begin playing chess.

How we got here

Ideating during our first visit with Steve

Our  initial visit with Steve, had successfully  produced plenty of ideas we could have used. Some of the original ideas centered around making the driving experience safer by keeping Steve alert at the wheel,a tracking device that would help him find his phone when it  gets lost and numerous devices that Steve could use to  teach other about the game of chess.

Originally we had focused on ways in which we  could create a system to locate Steve’s phone more personally effective than the  “find my iPhone app” . We considered creating a case made of led’s that would blink with the  push of a wireless button.  Steve currently doesn’t use a case because of the increase in size it adds so we continued to brainstorm ideas.

After some discussion among the group, we reflected  what Steve would personally find interesting. We acknowledged that most of our conversation had revolved around Steve’s love for chess. He enjoys all aspects: playing, teaching, competing, and exploring new variations.

Affinity map of our initial  ideas

Our first chess ideas revolved around  helping Steve teach others about the game of chess. We considered building a smart chess board that would show someone how to set up pieces and  show  all legal moves one to three moves in advance given a certain setup. We felt that this would allow Steve to teach students faster but also be a fun way to show how technology could be used to lower learning barriers.

After a few days of diagramming, consulting with aids, and wrapping our heads around the logic, we realized that this approach, while good, was both outside our computational and physical hardware abilities while remaining within the scope of the course, and ultimately not personally designed to help the need of our user;Steve,. Refocusing our goals on improving the game  of chess for Steve  led us to think about ways in which we could build a device that would  allow for Steve to play one of his favorite versions of chess, Fischer-random. Considering Steve currently takes an unnecessary amount of time  to randomize his Fischer Random setup, we explored ways in which we could efficiently scrambles the starting location of a players back row.

First functioning prototype.

First light and icon prototype

We created our first  LED prototype  that would demonstrate the basic idea behind the Fischer Random functionality.

It indicated where the randomized chess piece positions should be, as well as exemplifying our initial form concepts. We took this model to our critique with Steve and received an ample amount of feedback on how we could improve our design. This demonstration and critique of several ideas with Steve provided us a guiding star to follow throughout the rest of our design and making process.

While we were satisfied with  the overall concept  of the design , we  brainstormed with  Steve about ways in which we could  most effectively display  each piece.

Steve had suggested to use the typical chess piece symbols from the online versions of chess to ensure the images were legible.

We considered many display option including, RFID tagged pieces to LED backlit icons, LCD screens, laser-cut displays and many other ideas.

SPI linked LCD screens

After much discussion and ideation, we narrowed our design to testing two different methods for displaying the the set up for the back row. The first idea explored was to implement backlit icons of the various chess pieces.Initially, we made great progress on integrating and programming the lights beyond the original concept idea we showed Steve during our critique. However, the idea of using  LCD or OLED screens to display  each random ordered  piece icon seemed optimal . We were still leaning towards using LCD screens because the display size of icons would be larger and the form factor of the end device would be                                                                                            smaller and less bulky,

“Correctly” rendered bitmaps after a week of testing.

 We switched gears and began working with some LCD displays using the SPI protocol. They were rather frustrating to use, due to non-ideal libraries, incompatibility with the Arduino Uno’s native 5V logic level, and an extreme lack of good bitmap-conversion tools.

After a week of sleepless nights, we decided to cut our losses and returned our focus to making the backlit LEDs the best we could make them.

 

 

 

Second round of prototyping the correct layering of wood and acrylic.

We ran several experiments on how to make the back-lit chess icons look good and be legible to those with  even compromisable eyesight. We ended up choosing a layered system of frosted or etched acrylic and birch plywood that would dilute the  LEDs to create the effect and finish we sought.

Leads soldered onto LED strip

 

 

Some complications we faced while making the device were that the lights were unevenly spaced on the LED light strand we were using, which made the placement of holes more difficult. We were also hampered by significant differences in the thickness of the plywood versus the acrylic, which nominally should have been the same.

Testing full electronics setup

Testing electronics layout

Assembling the final product

The final difficulty was not the coding of the project, which came rather easily after thinking through the logic, but the assembly of the final product using glue, and mixed materials (acrylic and wood). We had quite a few material casualties, and the hand polishing took a long time.

After our final assembly we were excited to share this product with Steve.

Final touches for the prototype.

At our presentation, he became excited and mentioned that this is exactly what he wanted. He also noted how helpful this device would be at his chess club and for tournament play. This was a the moment of excitement and personal reward for our team. We recognized that we had truly met his need and created meaningful gift for him to use, now and into the future.

We did not stick to our final build plan, as we found it very difficult do decide between using small displays, or the cutouts that we eventually settled upon.  While diverging from the plan made the project more stressful, it did allow us to more carefully consider our options.

Conclusion & lessons learned

Critique major take-aways

Final critique & presentation of our product to Steve

There were three major suggestions that would have significantly improved the project.  First, when the device is first powered up it could turn on all of the lights to their appropriate color, allowing easy verification that the device is fully functional.  Second, a clear panel could be placed over the top of the device to protect from dirt and dust. Third, the lights and symbols could be mirrored on the other side of the device, and the device could be placed vertically in the center of the board, allowing both players to set up simultaneously.

  • These suggestions paired with our own thoughts would lead us to develop future considerations for building a similar device the future:
  • Go with name brand LED strips to ensure a greater likelihood of equidistant spacing between each light.
  • Integrating high resolution OLED screens would be effectivtive and decreasing the size and form factor of the end product.

What we learned

Building a device for someone else is much more rewarding than building one for yourself.

If we were to do this project over again we’d involve Steve more frequently throughout the process and go beyond just having a critique but actually conduct user testing in real environments where the device will be used. Though we kept in contact with Steve via email periodically our actual interaction were limited which could have added more value to our finished project

Working with an older adult is rewarding and your realize that they have 88% of the same needs you do only they are little older and may not know all of the possible functionality. Which in many ways highlights a design principle of building for the need not the exotic, even when the exotic is super cool.

Reflection

I’d recommend that everyone should have a chance to make something for a friend that  they’ve made who isn’t family or roommate and see how it changes their life and perspective on human relationships.

The most important and interesting part of this project wasn’t the coding and making of the end product but the process of how we got there and the friends we made along the way.

Building a project for a specific person puts a rather different perspective on the design.  Choices need to be made to best cater to the requirements of the intended user, rather than always choosing the most technically elegant solution.  I think the conflicts were generally resolved, but it did cause some dissonance in the design process.

Technical details

Code

/*
 * Copyright 2018, Alyssa Casamento, Josh LeFevre, Nathan Serafin.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
 */
#include &lt;Adafruit_NeoPixel.h&gt;;

constexpr int TDATA_PIN = 2;
constexpr int BDATA_PIN = 3;
constexpr int BUTTON_PIN = 4;
constexpr int SRAND_PIN = A0;

constexpr uint32_t
color(uint8_t r, uint8_t g, uint8_t b)
{
    return (((uint32_t) r) &lt;&lt; 16) | (((uint32_t) g) &lt;&lt; 8) | ((uint32_t) b);
}

constexpr uint32_t OFF_COLOR = color(0, 0, 0);
constexpr uint32_t READY_COLOR = color(0, 255, 0);
constexpr uint32_t ROOK_COLOR = color(255, 0, 0);
constexpr uint32_t KNIGHT_COLOR = color(0, 255, 0);
constexpr uint32_t BISHOP_COLOR = color(0, 0, 255);
constexpr uint32_t KING_COLOR = color(255, 255, 255);
constexpr uint32_t QUEEN_COLOR = color(255, 0, 255);

constexpr int NPIXELS = 24;
constexpr int ROW_SIZE = 8;

uint32_t READY_DISPLAY[] = {READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR,
                            READY_COLOR, OFF_COLOR, OFF_COLOR};

typedef enum {
    EMPTY = 0,
    KING = 1,
    QUEEN = 2,
    ROOK = 3,
    BISHOP = 4,
    KNIGHT = 5,
} piece_t;

bool prev_button = 0;
bool go = false;
bool pattern = false;

uint32_t tdisplay[NPIXELS];
uint32_t bdisplay[NPIXELS];

Adafruit_NeoPixel tstrip = Adafruit_NeoPixel(NPIXELS, TDATA_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel bstrip = Adafruit_NeoPixel(NPIXELS, BDATA_PIN, NEO_GRB + NEO_KHZ800);

static void
display_setup(Adafruit_NeoPixel&amp;amp;amp;amp;amp;amp;amp;amp;amp; strip, uint32_t c[])
{
    strip.setPixelColor(0, c[0]);
    strip.setPixelColor(1, c[1]);
    strip.setPixelColor(2, c[2]);
    strip.setPixelColor(3, c[3]);
    strip.setPixelColor(4, c[4]);
    strip.setPixelColor(5, c[5]);
    strip.setPixelColor(6, c[6]);
    strip.setPixelColor(7, c[7]);
    strip.setPixelColor(8, c[8]);
    strip.setPixelColor(9, c[9]);
    strip.setPixelColor(10, c[10]);
    strip.setPixelColor(11, c[11]);
    strip.setPixelColor(12, c[12]);
    strip.setPixelColor(13, c[13]);
    strip.setPixelColor(14, c[14]);
    strip.setPixelColor(15, c[15]);
    strip.setPixelColor(16, c[16]);
    strip.setPixelColor(17, c[17]);
    strip.setPixelColor(18, c[18]);
    strip.setPixelColor(19, c[19]);
    strip.setPixelColor(20, c[20]);
    strip.setPixelColor(21, c[21]);
    strip.setPixelColor(22, c[22]);
    strip.setPixelColor(23, c[23]);

    strip.show();
}

static void
display_back_row(Adafruit_NeoPixel& tstrip, Adafruit_NeoPixel& bstrip,
                 piece_t back_row[])
{
    for (int i = 0; i < ROW_SIZE; i++) {
        switch (back_row[i]) {
            case KING:
                tdisplay[i * 3]     = KING_COLOR;
                tdisplay[i * 3 + 1] = OFF_COLOR;
                tdisplay[i * 3 + 2] = OFF_COLOR;
                bdisplay[i * 3]     = OFF_COLOR;
                bdisplay[i * 3 + 1] = OFF_COLOR;
                bdisplay[i * 3 + 2] = OFF_COLOR;
                break;
            case QUEEN:
                tdisplay[i * 3]     = OFF_COLOR;
                tdisplay[i * 3 + 1] = QUEEN_COLOR;
                tdisplay[i * 3 + 2] = OFF_COLOR;
                bdisplay[i * 3]     = OFF_COLOR;
                bdisplay[i * 3 + 1] = OFF_COLOR;
                bdisplay[i * 3 + 2] = OFF_COLOR;
                break;
            case BISHOP:
                tdisplay[i * 3]     = OFF_COLOR;
                tdisplay[i * 3 + 1] = OFF_COLOR;
                tdisplay[i * 3 + 2] = BISHOP_COLOR;
                bdisplay[i * 3]     = OFF_COLOR;
                bdisplay[i * 3 + 1] = OFF_COLOR;
                bdisplay[i * 3 + 2] = OFF_COLOR;
                break;
            case KNIGHT:
                tdisplay[i * 3]     = OFF_COLOR;
                tdisplay[i * 3 + 1] = OFF_COLOR;
                tdisplay[i * 3 + 2] = OFF_COLOR;
                bdisplay[i * 3]     = KNIGHT_COLOR;
                bdisplay[i * 3 + 1] = OFF_COLOR;
                bdisplay[i * 3 + 2] = OFF_COLOR;
                break;
            case ROOK:
                tdisplay[i * 3]     = OFF_COLOR;
                tdisplay[i * 3 + 1] = OFF_COLOR;
                tdisplay[i * 3 + 2] = OFF_COLOR;
                bdisplay[i * 3]     = OFF_COLOR;
                bdisplay[i * 3 + 1] = ROOK_COLOR;
                bdisplay[i * 3 + 2] = OFF_COLOR;
                break;
    }
}

    display_setup(tstrip, tdisplay);
    display_setup(bstrip, bdisplay);
}

void
setup()
{
    pinMode(TDATA_PIN, OUTPUT);
    pinMode(BDATA_PIN, OUTPUT);
    pinMode(BUTTON_PIN, INPUT_PULLUP);

    pinMode(SRAND_PIN, INPUT);

    randomSeed(analogRead(SRAND_PIN));

    tstrip.begin();
    bstrip.begin();

    tstrip.setBrightness(64);
    bstrip.setBrightness(64);

    tstrip.show();
    bstrip.show();
}

void
loop()
{
    bool button = !digitalRead(BUTTON_PIN);
    if (button != prev_button) {
        delay(100);
        prev_button = button;
        if (button) {
            go = true;
            pattern = true;
        }
    }

    if (!go) {
       display_setup(tstrip, READY_DISPLAY);
       display_setup(bstrip, READY_DISPLAY);
    }
    else if (pattern) {
        piece_t back_row[8] = {EMPTY, EMPTY, EMPTY, EMPTY,
                               EMPTY, EMPTY, EMPTY, EMPTY};
        int K, Q, R1, R2, BI1, BI2;

        int color1_cnt = 4; // Even.
        int color2_cnt = 4; // Odd.

        /* Place king. */
        if (((K = random(1, 6)) % 2) == 0) {
            color1_cnt--;
        }
        else {
            color2_cnt--;
        }
        /* Place first rook. */
        if (((R1 = random(0, K - 1)) % 2) == 0) {
            color1_cnt--;
        }
        else {
            color2_cnt--;
        }
        /* Place second rook. */
        if (((R2 = random(K + 1, 7)) % 2) == 0) {
            color1_cnt--;
        }
        else {
            color2_cnt--;
        }

        back_row[K] = KING;
        back_row[R1] = ROOK;
        back_row[R2] = ROOK;

        /* Place first bishop. */
        BI1 = random(0, color1_cnt - 1);
        int n = 0;
        for (int i = 0; i < ROW_SIZE; i += 2) {
            if (back_row[i] == EMPTY) {
                if (BI1 == n) {
                    back_row[i] = BISHOP;
                    break;
                }
                n++;
            }
        }

        /* Place second bishop. */
        BI2 = random(0, color2_cnt - 1);
        n = 0;
        for (int i = 1; i < ROW_SIZE; i += 2) {
            if (back_row[i] == EMPTY) {
                if (BI2 == n) {
                    back_row[i] = BISHOP;
                    break;
                }
                n++;
            }
        }

        /* Place queen. */
        Q = random(0, 3);
        n = 0;
        for (int i = 0; i < ROW_SIZE; i++) {
            if (back_row[i] == EMPTY) {
                if (Q == n) {
                    back_row[i] = QUEEN;
                    break;
                }
                n++;
            }
        }

        /* Place knights. */
        for (int i = 0; i < ROW_SIZE; i++) {
            if (back_row[i] == EMPTY) {
                back_row[i] = KNIGHT;
            }
        }

        display_back_row(tstrip, bstrip, back_row);
        pattern = false;
    }
}

GitHub repository

Schematic

Full resolution

Physical form .dxf file

 

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/team-steve-final-documentation/feed/ 0
A Natural Meal Adaptation by Team Renfrew: Final Documentation https://courses.ideate.cmu.edu/60-223/s2018/work/team-renfrew-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/team-renfrew-final-documentation/#respond Thu, 10 May 2018 15:33:19 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3575

The goal of our final project was to incorporate all of the electrical, mechanical, and design skills we have been developing throughout the semester to create an assistive device for an older person. The assistive device was meant to address a problem in our older person’s life. However, after talking with Rosemarie (our older friend) for several hours, we realized that she was enjoying life to the fullest and did not really find anything to be too problematic.  As a result, our group, Team Renfrew, decided to create a device that would incorporate two of Rosemarie’s greatest fascinations: cooking and interactive art. We call our device: A Natural Meal Adaptation. This device simulates an LED light representation of a meal that Rosemarie is going to prepare, so she can set the mood while cooking. She can also refer to meals she has made before and be reminded of the beautiful light pattern that was displayed, and whether her husband Joe enjoyed that specific meal. Continue reading this post to learn more about our device, how it works, and our entire design process along the way.

Read more about the early stages of our process here:

Initial Meeting with Rosemarie/Ideation:  https://courses.ideate.cmu.edu/60-223/s2018/work/meeting-documentation_1/

Our First Iteration: https://courses.ideate.cmu.edu/60-223/s2018/work/process-documentation_team-renfrew/

 

What We Built

Light display of Tiramisu. How yummy!

Base display and accompanying controller turned off

Light display of Angry Pasta. I wonder what makes the pasta angry.

Our project begins by asking Rosemarie a few questions about the dish she is about to prepare. Based on her answers to those questions, a distinctive light pattern gets uploaded to our tree and base display. Once she is done viewing the light pattern display, she may save the name of the dish and the light pattern associated with it for future reference. When she is looking back at old displays, she may even press the Favorite button, to remind her that the dish was a crowd-pleaser (or a Joe-pleaser).

Close-up of the Favorite button on the base display. Let the chef know you were a fan of the dish with this button!

Close-up of the Flag LEDs. The Italian flag colors have never looked so good.

Close-up of the natural aesthetic of our project. Check out those living hinges!

This is how we envisioned Rosemarie using this project:

Rosemarie begins preparing for her evening by deciding which vegan dish she is going to make. She decides that tonight she is going to make Garlic Pasta with Roasted Cajun Cauliflower. She turns on her A Natural Meal Adaptation and flips the Mode switch on the controller to Record mode. She starts by answering a few questions on the LCD screen about her dish. She answers each question by either pressing the Yes button or the No button. (What cuisine? – ITALIAN!, Hot or cold? – HOT!, What flavor? – SAVORY!, Heavy or light? – HEAVY!) To her amazement, the lights on the display begin to flash according to her inputs. While she cooks and serves her dish, the lights continue to dance around the tree and shine on the base display. After she’s done enjoying the meal with her husband, she flips the Mode switch on the controller to Display mode and saves a name for the dish using the Letter Wheel. (G-A-R-L-I-C P-A-S-T-A) She uses the Previous and Next buttons on the controller to view dishes she has made in the past, hoping to get inspiration for what she might cook tomorrow. She flips back to her GARLIC PASTA dish and reflects for a moment. Joe presses the Favorite button on the base, indicating that tonight’s meal was a success. A new light on the base begins to shine red, and Rosemarie is satisfied with the events of the night. She unplugs her A Natural Meal Adaptation and puts it away, sure of the fact that it will remember the dishes she has saved next time she starts it back up again.

A user switching the mode of the device from Display to Record

A user pressing the favorite button causing the Favorite LED to light up red. It must’ve been a tasty dish!

A user pressing the No button prompting for a different choice. Some Asian food sounds good right now.

 

How We Got Here

After we had fully realized our final iteration of the project, it was amazing to look back at how far we had come. This latest iteration was almost unrecognizable from what we had presented during our prototype presentation. Many of the revisions we decided to make for our project came from direct feedback that Rosemarie provided us. One of the first revisions we decided to make to our original idea was to change the form. Rosemarie thought that the 3×3 grid of RGB LED modules was nice, but after some brainstorming, we decided that she’d enjoy a more natural display like a tree much more. A tree display would mesh well with the other plants currently in her apartment. Since we changed the form to that of a tree, it also made sense to use different LEDs, so we decided to use RGB LED pixel strands so we could fit more smaller lights onto our display, while also being able to keep the circuitry fairly hidden.

Monica testing out our new LEDs

Early skeleton of our new natural form

Planning out our pin assignments so we don’t forget them later

Another inspiring idea Rosemarie had for us, was to implement some kind of indication of whether or not her husband, Joe, liked the dish she made. We thought that this was an excellent idea because it added to the interactive experience of the device, and would be a useful reminder to Rosemarie of which dishes were most successful. Our first thought was to have a heart button that served as a button and an LED that would light up when pushed, however, we realized that finding a specific electronic part that would meet our desired expectations would be difficult. Thus, we decided to pivot our idea and separate a Favorite button and a Favorite LED. If Joe (or anyone really) liked a dish, he would press the Favorite button while the dish was being displayed in Display mode. This would cause a Favorite LED to flash red (the color of love), and each time that dish was being displayed, the Favorite LED would always be lit. It took us some time to decide when in our code it would be best to check for the Favorite button to be pressed, and we decided that the most appropriate place would be in Display mode, while old dishes were being displayed. It made the most logical sense to check for this input here, while also being an easy place to incorporate functionality in the software.

Favorite LED shining red indicating the meal was a success!

The most challenging revision we needed to make for our final project was adding the functionality of a Display mode. Up until our prototype iteration, we had only enabled a user to give inputs to the device to display a light pattern, however, we had no mechanism for remembering old light patterns and the dishes associated with them. Since we were all new to the Arduino’s EEPROM memory management, we found it difficult to debug areas of our code that utilized this memory. Additionally, we kept encountering a problem where it appeared that our code would enter an infinite loop causing our LCD displays to flash on and off, through no provocation from the user.

Arushi tirelessly debugging the code

Disassembling the controller to figure out what is going on with our LCD displays

Initially, we were able to stick to our final build plan as we individually constructed modular pieces of our code, hardware, and display. However, as we got closer to our final critique, it became difficult to stick to the plan. The night before the critique, we had all the code assembled and the form constructed, but we had yet to debug. The issues mentioned in the previous paragraph were our biggest challenges, but we knew that having a faulty device was not an option. We couldn’t let Rosemarie down, and through sheer determination, hours of mindlessly reading through code, and a few snack breaks, we were able to create a working final product.

All the pieces coming together

Rosemarie enjoying her new A Natural Meal Adaptation 

Conclusions and Lessons Learned

One piece of recurring feedback we got for our final project, was that separating user inputs to the base display and the controller made the project less user friendly. This was actually a design choice that we thought a lot about. We eventually went with putting our Letter Wheel and Favorite Button on the base display, and keeping the other inputs (Yes, No, Previous, Next, and Mode switch) on the controller, so that the controller wouldn’t be cluttered with buttons. For a future iteration of the project, we might explore the possibility of reducing the amount of inputs entirely, or providing multiple functionalities for certain inputs.

Another piece of feedback we got was to increase the ease of typing a dish name. An obvious solution would be to add a keyboard, but we actually did look into this and had a hard time finding a keyboard that was compatible with the Arduino and did not take up too much real estate on our display or controller. This is why we decided to use a Letter Wheel, because it was a quicker way of typing in a word, instead of using an arcade style method where we have the user click No repeatedly as the first letter changes until the correct letter is shown, and repeating until the entire word is formed. For a future iteration of the project, we may try to do more research to find a keyboard or a similar means of entering a dish name more easily.

One more thing that we might try to address in a future iteration of this project, would be to use different sensors so we could gather data about a dish without requiring the user to answer a bunch of questions. This was our initial hope for the project when it was conceived, however, we ran into a lot of obstacles of getting reliable data from sensors (temperature, sound) that would help distinguish different meals.

When you are surrounded by people that are very similar to you all the time, you end up losing some creative ideas that could’ve been formed if you had spoken to someone very different from you. This is why we had such a great time working with Rosemarie, because she lives her life very different from that of a college student. It was refreshing to hear her perspectives on life, be able to brainstorm ideas for our project, and discuss ways we could take our project to the next level. A Natural Meal Adaptation was designed with Rosemarie’s needs in mind and being able to see the delight in her eyes as she interacted with our project felt extremely fulfilling. Hopefully, you can take some time out of your day to talk with someone different from you, because in the end you might just create something great!

 

Team Renfrew (Arushi, Adriel, and Monica) and their number one inspiration, Rosemarie

Schematic:

Link to code and laser cutting files: https://github.com/arushibandi/60-223/tree/master/final_proj_code

]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/team-renfrew-final-documentation/feed/ 0
FitBox by Enrique’s Gizmos: final documentation https://courses.ideate.cmu.edu/60-223/s2018/work/fitbox-by-enriques-gizmos-final-documentation/ https://courses.ideate.cmu.edu/60-223/s2018/work/fitbox-by-enriques-gizmos-final-documentation/#respond Wed, 09 May 2018 05:13:48 +0000 https://courses.ideate.cmu.edu/60-223/s2018/work/?p=3537 This class, Introduction to Physical Computing, has covered both the technical and design of electronic devices in depth.  These teachings and practice culminated in this final project: creating an assistive device for someone else. Specifically, these devices were designed for older individuals who we could have a significant impact on.  Our group, Enrique’s Gizmos, collaborated with an older individual, Enrique, in order to ideate and develop our device. Initially, we met with Enrique to brainstorm different challenges and annoyances in his life that we could possibly solve.  This meeting concluded with our challenge: to create a device that could help motivate older individuals to exercise. This meeting was documented here: https://courses.ideate.cmu.edu/60-223/s2018/work/initial-meeting-documentation/ .  After deliberating and prototyping different possibilities to achieve this, our group decided to create the FitBox.  The FitBox is a fitness tracker, planner, and motivator. Some initial circuits and designs were brought to a showcase to be given feedback.  This meeting was documented here: https://courses.ideate.cmu.edu/60-223/s2018/work/team-enriques-gizmos/ .  After receiving feedback on our design for the FitBox, we worked towards the final iteration, and showcased the device and how it could help to Enrique, the other older individuals who worked with the other groups, and other participants at a design fair.

 

The FitBox is a fitness planner, tracker, and motivator.  It is a wearable, rechargeable, device similar in design to a beeper.  Firstly, the device creates an exercise routine based off of different exercises including goals and rests which are displayed on a screen on the front.  The goals of these exercises change depending on the user’s previous performance. For example, if the user completed all repetitions of an exercise, the next time the exercise is done, the goal number of repetitions would be higher.  If the user had to skip some repetitions, the goal would be lower next time. The FitBox also tracks a users performance. It can act as a timer or track and record steps or repetitions of an exercise. When the user completes an exercise, a melody is played to notify them that they completed the exercise, and to provide a congratulations to help motivate them to continue exercising.  The device motivates by recording previous personal records on exercises, too. It stores all this information and, even if the device loses power, will remember it when it is powered again through its rechargeable battery.

 

The Fitbox in its entirety

The back of the Fitbox has velcro straps to hook around your belt

The inside of the Fitbox is very compact with very few wires

Different angle of the inside of the Fitbox

The LCD lets you know when the Fitbox is setting up (calibrating the accelerometer)

Going through the planking exercise

When you finish an exercise, rest for 15 seconds

When you press “pause”, this screen pops up

 

When Enrique decides to begin his exercise for the day, he goes to his shelf, where his fitbox sits. He puts the fitbox on his belt, and pushes the “play” button to begin his routine.  A countdown begins and he readies himself for exercise. When the countdown completes, the screen changes and tells him to do a plank for thirty seconds. After that time is up, the box plays a jingle to tell him that this time is up. After a short resting period the fitbox prompts him that is is time for the next exercise. He completes a few more exercises, but gets tired and decides to skip his daily walk of 1000 steps. At the end of that day’s workout routine, the screen tells him that he has set a new personal record for planking.

The next day he returns to his fitbox for his daily exercise. After the initial countdown, the first exercise begins: planks. However this time he notices that his goal has increased to thirty-two seconds. He feels good about the progress he is making. That day when he reaches that walking portion, Enrique sees that his goal has decreased. He feels like he can accomplish this new walking goal and sets off on an enjoyable stroll.

 

The device we decided to create to tackle the problem of fitness motivation, was, admittedly, a complex one.  The features we deemed necessary such as a compact size to make it wearable and motion tracking proved rather difficult, and the road to our final FitBox was wrought with trial-and-error, difficult decisions, and breakdowns.  In the end, we had to consider what was necessary and what wasn’t for a minimally viable product. Our finished device expanded beyond the minimum, but different features had to be removed or changed along the way.

It should also be noted that our final build plan including our schedule and final features of the device changed, as well.  They proved useful to keep the design and build process on track and working toward a known goal, but as progress was made and difficulties found, both had to be changed to fit the current situations.

An initial major hurdle was creating a method of recording motion.  One of our major design parameters was ergonomics. We wanted the FitBox to be something that Enrique could use without much difficulty or interference into his normal routine: if it was inconvenient to use, then it likely wouldn’t be used.  We thought of two feasible means of recording motion. The first was to have some type of button or pressure sensor that could be put on the bottom of one’s shoe. The second was to use an accelerometer to record and compare the accelerations of the device and correlate them with a movement.  After some serious discussion, the latter option was chosen. The pressure sensor would likely be more robust and simpler, but it would limit the types of motions that could be recorded to ones where pressure of the user’s foot on the floor changes such as walking or squats. It also would have been quite cumbersome and unseemly as wire or special shoe covering/attachment would be needed.  This would get in the way of the user’s normal range of motion and would push them away from using the device. The accelerometer was more complex, but would be easier to integrate into the system and would open up the device to a much wider range of applications.

This decision was maintained to the final product, but the complexities of the accelerometer and signal processing did create multiple challenges and effected the final use of the device.  The actual processing of the accelerations read proved, in theory, much simpler and robust than was expected. It simply compared what it saw when the user was exercising to what it read when the user wasn’t.  To our surprise, this method was able to fairly accurately measure repetitions of most movements whether moving fast or slow. Other features had to be created to accommodate this, though, and this did affect the final FitBox by causing the user to have to stand still for approximately 15 seconds so as to allow the FitBox to figure out what it looked like when the user was standing still.  This was a slight, but, seemingly necessary inconvenience that was created for the user in the final device. The development of this also pushed our build off schedule by one or two days from final build plan.

Working on optimizing the circuitry, with prototype Fitbox shown

One of the most impactful issues was not creating a device that worked, however.  It was creating a device that was usable based on the criterion stated above: specifically compact and wearable.  These characteristics that we deemed necessary for the final device to be a success greatly impacted our timeline and gave rise to many unforeseen issues.

In order to create a device that was as small as possible, all of the electronics had to be packed as close together as possible in a housing that would hold it in that shape.  This involved trying to decrease the number of wires used and attach all of the electronic components to thin boards. Attaching the electronics to the board was not difficult and was done fairly quickly.  Designing how to pack the electronics as close together as possible, though, took quite a bit of time and planning. In our final design plan, we expected designing how the circuit would be placed inside the housing to take only a day or less, but it ended up taking two or three days due to the limitations on ways we could connect and place the electronics in such constrained quarters.

How we wanted the layout to fit in the box

After soldering the Arduino Nano, Piezo buzzer, and accelerometer

How the buttons on the front panel were soldered to the rest of the circuit

We thought Enrique might not be wearing a belt the day of the final crit so we fashioned a belt out of rope for him

Fitting the LCD screen into the front panel

After soldering everything, we needed to dimension the walls of the box so we held everything together

The finalized Fitbox

After creating a reasonably sized design and creating a housing to fit around it, other issues due to the cramped size arose.  Most notably was a significant bug in the system where, if the device was moved too vigorously, the program and device would freeze.  We spent days trying to solve this problem that we never predicted would happen. It appears that the issue is possibly due to a connection that shakes loose when moved, however, after inspection both visually and with multimeters, the bad connection could not be found.  The complexity of solving this problem along with time constraints forced us to try and minimize the problem instead. By placing all of the circuitry inside the housing and securing it as well as possible, we reduced movements of electronics relative to each other. This drastically reduced how often it froze.  This issue, however, used up the final couple of days that we had planned for some simple exercise trials and a few other simple features like a low battery light. These features could not be added due to the freezing issue and, instead, we focused on creating a final product that was efficacious in its base purpose: to plan, track, and motivate exercising.  In further iterations, an integrated circuit would likely help negate this issue and the other extra features could be added to overall better the FitBox experience.

One of the preeminent features that had to be removed in order to create a viable device due to these time constraints was the dock.  In the final design plan, the dock would be a place the FitBox could plug into to give it more functionality (a menu to choose exercises, more buttons) and to charge at.  Due to the time constraints caused by the above issues and challenges, this aspect had to be completely neglected. This led to other features such as a sleep mode to also not be incorporated.  There wasn’t enough time to both deal with the above issues and add these many planned, useful features. Due to time constraints, efficacy and viability had to be held paramount.

 

Final Thoughts:

There were three major suggestions that were common in our feedback. First, the buzzer used should have been easier to hear. Second, people would like the ability to add or remove exercises from the list. Third, a more ergonomic design was desired. In regards to the louder buzzer, during most of our testing we were using the device without a completed enclosure in a small, fairly quiet room. At the crit, we had finished putting the top wood panel on the fitbox, there was significant background noise, and the room was high ceilinged. We had not noticed how quiet the buzzer was before that, but in the crit environment, it was barely audible. This would be fairly easy to change by simply switching the buzzer for a more powerful model and cutting small holes for the sound to be heard.

A number of people also commented on the physical form of the fitbox. Although it was small compared to its features, it could have been made smaller and more ergonomic. Specifically, moving the screen to the top of the box would have allowed the used to read the information displayed when actually wearing the device.  We hadn’t thought about that, and that would significantly change our project, especially when it comes to assembling the hardware. Our final version was taped together to allow for ease of access to the electronics (namely, the reset button), but this attachment method left the fitbox feeling unrefined. Both of these changes would be easy to implement, but we ran out of time for the final critique.  

Many people also asked for the ability to add or remove exercises. We were planning to do that with a dock so that the actual fitbox wouldn’t need extra buttons, but unfortunately we also ran out of time to do that. We definitely wanted this function as well, because we didn’t know what Enrique would want and we wanted to make sure that we covered all the bases to ultimately create a product that he would use.

One major takeaway from working with other people was that we’re very similar and it felt very natural to discuss these projects and daily life problems with them although we do see different problems within designs. It surprised us that Enrique didn’t consider himself as older and couldn’t really come up with any problems he had but he was able to suggest a lot of ideas for his friends who are presumably around the same age. We wished we could have met with him more, it might have been better to start the final project in the beginning of the semester by just meeting our older friends and brainstorming to flesh out ideas but not actually build anything until the end of the semester. That would help with gathering techniques throughout the semester because we would have in mind what we want to make, sort of like reading a homework assignment before the material is taught in class.

Overall, this project was an incredible learning experience.  We decided to tackle a fairly complex problem and some complex technology, but this added to the experience.  Through trial-and-error, debugging, and designing a technology such as the FitBox, we pushed our technical and design skills: testing what we could do.  This did cause some instances where design and ergonomics were held at the forefront while practicality and timeliness fell to the wayside, but this helped us learn all the more.  From being pressed by time and technology, we had to learn how to manage time, how to reign in an idea and create a minimally viable product, and had to solve problems we never could have predicted.  Our design knowledge also was pushed by designing the FitBox for someone else. Working with Enrique was delightful and we are very proud of what we were able to create.

Schematic

#include <EEPROM.h>

#include <MPU6050_tockn.h>

#include <MPU6050_tockn.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "pitches.h"


MPU6050 mpu6050(Wire);
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// notes in the melody:
int melody[] = {
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 4, 4, 4, 4, 4
};

const int playButton = 5; //switch for play/pause
bool play = 0; //
bool buttonPressed = 0;
bool finishedForTheDay = 0;
bool countdownPlayed = 0;
const int skipButton = 8;
const int piezoPin = 7;
const int randomizeButton = 6;
const int muteButton = 9;
const int charger = 11;

long timer = 0;  //generic timer
long dockTimer = 0;
int steps = 0;  //number of steps
int sslimit = 155; //limit that determines if user is standing still or moving
int stepOn = 0;  //generic variable I used to check if the user is mid-step
bool mute = 0;
bool charging = 0;

//exercise = [accel threshold, goal rep, current rep, skip/paused, personal record]
//int pause[5] = {0, 0, 0, 0, 0};
const int numExercises = 7; //number of exercises including pause

//int exercises[][5] = { {pause},
//  {walk}, {squats}, {calfRaise}, {stepUps},
//  {straightLegRaise}, {bentKneeRaise}, {backExtension}
//};
unsigned int exercises[numExercises][5] = {
  {sslimit, 1000, 0, 0, 1000}, //0. walk
  {sslimit, 10, 0, 0, 10}, //1. squat
  {sslimit, 6, 0, 0, 6}, //2. calf raises
  {0, 10, 0, 0, 10}, //3. arm raises
  {0, 600000, 0, 0, 50000}, //4. biking
  {sslimit, 20, 0, 0, 10}, //5. rowing
  {0, 900000, 0, 0, 10000}, //6. dancing
};

String nameExercises[numExercises] = {
  "walk ",
  "squat ",
  "calf raise ",
  "arm raise ",
  "biking ",
  "rowing ",
  "dance ",
};

int pausedTime = 0; //keeps the time when pause is pressed
int function = 0; //used to keep track of what was printed

const int numDailyExercises = 7; //number of exercises he should do daily
int dailyExercise[numDailyExercises] = {3, 2, 1, 4, 5, 0, 1}; //keeps track of which activities he should do for the day
int currentExercise = 0; //index to note which exercise is currently being done in dailyExercise
int currentReps;
int goal;  //goal number of steps
int goalPlc = 0;  //where to write the next segment on the display
int goalWrt = 0;  //variable to make sure the user has a different number of steps before writing another segment
int i = 0; //index for keeping track of which PR was printed


void setup() {
  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(piezoPin, melody[thisNote], noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(piezoPin);
  }
  Serial.begin(9600);
  //saveData();
  getData();

  lcd.init();  //initializes lcd
  lcd.backlight();  //turns on lcd backlight
  lcd.print("Setting up");
  lcd.setCursor(0, 1);
  lcd.print("Please dont move");
  Wire.begin();
  pinMode(13, OUTPUT);
  pinMode(playButton, INPUT_PULLUP);
  pinMode(skipButton, INPUT_PULLUP);
  pinMode(randomizeButton, INPUT_PULLUP);
  pinMode(muteButton, INPUT_PULLUP);
  pinMode(charger, INPUT);
  digitalWrite(13, HIGH); //to visualize set up with Arduino's onboard LED
  mpu6050.begin();  //begins accelerometer setup
  mpu6050.calcGyroOffsets(true);  //calculates gyroscope starting position
  recal(); //recalibrates acceleration and sets new steady state value
  digitalWrite(13, LOW); //turns off onboard LED when initial calibration is done
}

void loop() {
  //check if charging first
  if (digitalRead(charger)) {
    charging = 1;
  }
  if (!charging) {
    if (!digitalRead(playButton) && !buttonPressed) {
      Serial.println("BUTTON PRESSED");
      buttonPressed = 1;
      play = !play;
    }

    else if (digitalRead(playButton)) {
      buttonPressed = 0;
    }

    if (play) {
      if (!finishedForTheDay) {
        Serial.println("playing");
        if (playTracking()) {
          finishedReps();
        }
      }
      if (finishedForTheDay) {
        resetExercises();
      }
    }

    if (!play || finishedForTheDay) {
      //Serial.println("paused");
      pauseTracking();
    }

    if (!digitalRead(skipButton)) {
      skipExercise();
    }

    if (!digitalRead(muteButton)) {
      mute = 1;
    }

    if (digitalRead(muteButton)) {
      mute = 0;
    }
  }

  else if (charging) {
    //dockMenu();
    if (!digitalRead(randomizeButton)) {
      randomizeExercises();
    }
  }
  //Serial.println("function: " + String(function));

}


//recalibrates
void recal() {
  lcd.clear();
  lcd.print("Setting up");
  lcd.setCursor(0, 1);
  lcd.print("Please dont move");
  int ssXmax = -500;  //generic placeholder variables
  int ssYmax = -500;
  int ssZmax = -500;
  timer = millis();
  while (millis() - timer < 5001) { if ((millis() - timer) % 50 == 0) //reads the accelerometer every 50 milliseconds for 5 seconds and makes placeholders maximums { int xacc = mpu6050.getAccX() * 100; int yacc = mpu6050.getAccY() * 100; int zacc = mpu6050.getAccZ() * 100; if (xacc > ssXmax) {
        ssXmax = xacc;
      }
      if (yacc > ssYmax) {
        ssYmax = yacc;
      }
      if (zacc > ssZmax) {
        ssZmax = zacc;
      }
    }
  }
  sslimit = ssXmax + ssYmax + ssZmax + 10; //adds maximum accelerations with a bias term to create standard ssLimit to compare readings to
  for (int i = 0; i < numExercises; i++) { //update the acceleration for each repped exercise if (exercises[i][0] * 100 > 0) {
      exercises[i][0] = sslimit;
    }
  }
}

void countdown() {
  countdownPlayed = 1;
  if (function != 1) {
    lcd.clear();
    lcd.setCursor(14, 1);
    lcd.print("||");
    lcd.setCursor(0, 0);
    lcd.print("Exercise will");
    lcd.setCursor(0, 1);
    lcd.print("begin in ");
    function = 1;
    timer = millis();
  }
  while (millis() <= timer + 5000) { //print every second if ((millis() - timer) % 1000 == 0) { //print time left until starting lcd.setCursor(9, 1); lcd.print(String(5 - ((millis() - timer) / 1000)) + " "); } } //delay(250); } //run fxn when unpaused (button press) bool playTracking() { //Serial.println("sslimit: " + String(sslimit)); //countdown only before first exercise of the day if (!countdownPlayed) { //currentExercise = 0; countdown(); } lcd.setCursor(14, 1); lcd.print(">>");
  int foo = dailyExercise[currentExercise]; //for easier reading
  //Serial.println("foo: " + String(foo));
  //Serial.println("testing: " + String(walk[1]));
  Serial.println("limit: " + String(exercises[foo][0]));
  //Serial.println("goal: " + String(exercises[foo][1]));
  //Serial.println("current: " + String(exercises[foo][2]));
  //Serial.println("PR: " + String(exercises[foo][4]));
  Serial.println("current exercise: " + String(currentExercise));
  int goal = exercises[foo][1];  //goal number of reps
  //int goalSeg = goal / 16; //number of steps per display segment lighting up

  mpu6050.update();  //updates accelerometer with new reading
  //check if this is a timed exercise or not
  if (exercises[foo][0] * 100 > 0 && millis() - timer > 50) //reads accelerometer every 50 milliseconds
  {
    int xacc = mpu6050.getAccX() * 100; //reads accelerometer X,Y, and Z accelerations and scales them up by 100 to better see on the plotter
    int yacc = mpu6050.getAccY() * 100;
    int zacc = mpu6050.getAccZ() * 100;
    Serial.println(String(mpu6050.getAccX() * 100) + " " + String(mpu6050.getAccY() * 100) + " " + String(mpu6050.getAccZ() * 100) + " " + String(exercises[foo][2] * 10)); //prints out data for plotter
    if (stepOn == 0) { //adds a step if reading is above ssLimit; toggles on/off if user croses the ssLimit threshold
      if (xacc + yacc + zacc > exercises[foo][0]) {
        exercises[foo][2]++;
        stepOn = 1;
      }
    }
    if (xacc + yacc + zacc < exercises[foo][0]) {
      stepOn = 0;
    }
    if (function != 2) {
      lcd.clear();
      Serial.println(nameExercises[foo]);
      lcd.print(nameExercises[foo] + String(exercises[foo][1]) + "   ");
      lcd.setCursor(0, 1);
      lcd.print("Current: ");
      function = 2;
    }
    lcd.setCursor(9, 1); //sets cursor to after "Current: "
    lcd.print(String(exercises[foo][2]));  //writes number of reps
    timer = millis();  //updates timer
  }

  //if this is a timed exercise, then dont count reps, count time
  if (exercises[foo][0] == 0) {
    Serial.println("timed function");
    if (function != 3) {
      timer = millis();
      lcd.clear();
      if (exercises[foo][1] < 60000) { lcd.print(nameExercises[foo] + String(exercises[foo][1] / 1000) + "s "); } else if (exercises[foo][1] >= 60000){
        lcd.print(nameExercises[foo] + String(exercises[foo][1] / 60000) + "min");
      }
      lcd.setCursor(0, 1);
      lcd.print("Current: ");
      function = 3;
    }
    exercises[foo][2] = millis() - timer + pausedTime;
    if (millis() > timer + 1000) {
      lcd.setCursor(9, 1); //sets cursor to after "Current: "
      lcd.print(String(exercises[foo][2] / 1000));
    }
  }

  //check if goal was hit for that exercise
  if (exercises[foo][2] >= exercises[foo][1]) {
    return true;
  }
  return false;
}


void finishedReps() {
  int foo = dailyExercise[currentExercise]; //for easier reading
  if (!mute) {
    for (int thisNote = 0; thisNote < 8; thisNote++) {

      // to calculate the note duration, take one second divided by the note type.
      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
      int noteDuration = 1000 / noteDurations[thisNote];
      tone(piezoPin, melody[thisNote], noteDuration);

      // to distinguish the notes, set a minimum time between them.
      // the note's duration + 30% seems to work well:
      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:
      noTone(piezoPin);
    }
  }

  if (function != 4) {
    lcd.clear();
    lcd.print("You're done with");
    lcd.setCursor(0, 1);
    lcd.print("this exercise!");
    function = 4;
    delay(3000);
  }

  //if this is the last exercise then say you're done for the day
  if (currentExercise == numDailyExercises - 1) {
    pausedTime = 0;

    finishedExercise();
  }

  //if it's not the last exercise then move onto the next one
  else if (currentExercise < numDailyExercises) {
    if (function != 6) {
      lcd.clear();
      lcd.print("Rest for 15 sec");
      timer = millis();
      while (millis() <= timer + 15000) {
        //print every second
        if ((millis() - timer) % 1000 == 0) {
          //print time left until starting
          lcd.setCursor(0, 1);
          lcd.print(String(15 - ((millis() - timer) / 1000)) + " ");
        }
      }
      delay(250);
      function = 6;
    }
    currentExercise++;
    lcd.clear();
    lcd.print("Moving on");
    if (exercises[foo][0] == 0) {
      timer = millis();
    }
    pausedTime = 0;
    delay(2000);
  }
}


//runs when switch is on pause
void pauseTracking() {
  play = 0;
  if (function != 7) {
    lcd.clear();
    lcd.print("PAUSED");
    Serial.println("paused");
    lcd.setCursor(0, 1);
    lcd.print("press to play");
    lcd.setCursor(14, 1);
    lcd.print("||");
    //delay(3000);
    if (finishedForTheDay) {
      lcd.setCursor(0, 1);
      lcd.print("done for the day");
      delay(2000);
      lcd.clear();
      lcd.print("press play");
      lcd.setCursor(0, 1);
      lcd.print("to reset");
      //delay(1000);
    }
    function = 7;
  }

  exercises[dailyExercise[currentExercise]][3] = 1;
  //if it's a timed exercise then record the time when it was paused
  if (exercises[dailyExercise[currentExercise]][0] == 0) {
    pausedTime = exercises[dailyExercise[currentExercise]][2];
  }
}


//runs when skip button is pressed
void skipExercise() {
  if (function != 9) {
    lcd.clear();
    lcd.print("skipped exercise");
    function = 9;
  }
  exercises[dailyExercise[currentExercise]][3]++;
  if (currentExercise == numDailyExercises - 1) {
    pausedTime = 0;
    if (!mute) {
      for (int thisNote = 0; thisNote < 8; thisNote++) {

        // to calculate the note duration, take one second divided by the note type.
        //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
        int noteDuration = 1000 / noteDurations[thisNote];
        tone(piezoPin, melody[thisNote], noteDuration);

        // to distinguish the notes, set a minimum time between them.
        // the note's duration + 30% seems to work well:
        int pauseBetweenNotes = noteDuration * 1.30;
        delay(pauseBetweenNotes);
        // stop the tone playing:
        noTone(piezoPin);
      }
    }
    finishedExercise();
  }
  currentExercise++;
  pausedTime = 0;
  delay(1000);
}

void finishedExercise() {
  saveData();
  if (!mute) {
    for (int thisNote = 0; thisNote < 8; thisNote++) {

      // to calculate the note duration, take one second divided by the note type.
      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
      int noteDuration = 1000 / noteDurations[thisNote];
      tone(piezoPin, melody[thisNote], noteDuration);

      // to distinguish the notes, set a minimum time between them.
      // the note's duration + 30% seems to work well:
      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:
      noTone(piezoPin);
    }
  }
  if (function != 8) {
    lcd.clear();
    lcd.print("You finished");
    lcd.setCursor(0, 1);
    lcd.print("for today!!");
    delay(3000);
    function = 8;
  }
  finishedForTheDay = 1;
  pauseTracking();
}


void randomizeExercises() {
  for (int i = 0; i < numDailyExercises; i++) { dailyExercise[i] = random(0, numExercises); } Serial.println("Exercises randomized!"); } void showPR() { if (millis() > dockTimer + 30000) {
    Serial.println(exercises[dailyExercise[i]][4]);
    dockTimer = millis();
    i++;
    if (i == numDailyExercises) {
      i = 0;
    }
  }
}


void resetExercises() {
  lcd.clear();
  lcd.print("resetting");
  delay(1000);
  //lcd.print(String(hour()) + ":" + String(minute()));
  //make new reps and reset current reps
  for (int i = 0; i < numExercises; i++) { //check if PR was beat if (exercises[i][2] > exercises[i][4]) {
      exercises[i][4] = exercises[i][2];
      lcd.clear();
      lcd.print("You beat your");
      lcd.setCursor(0, 1);
      lcd.print(nameExercises[i] + " PR");
      delay(1500);
      Serial.println(exercises[i][4]);
      lcd.clear();
      lcd.print("Your new record");
      lcd.setCursor(0, 1);
      lcd.print("is: ");
      if (exercises[i][0] * 100 > 0) {
        lcd.print(String(exercises[i][4]));
      }
      else if (exercises[i][0] * 100 == 0) {
        lcd.print(String(exercises[i][4] / 1000));
      }
      //function = 5;
      delay(2000);
    }
    //if exercise was skipped/paused, decrease number of reps
    if (exercises[i][3] == 1) {
      if (exercises[i][0] * 100 > 0) {
        if (exercises[i][1] > 2) {
          exercises[i][1] -= 2;
        }
      }
      else if (exercises[i][0] == 0) {
        if (exercises[i][1] > 2000) {
          exercises[i][1] -= 2000;
        }
      }
      exercises[i][3] = 0;
    }
    //if not skipped, increase reps by 2 and time by 2 sec
    else if (exercises[i][3] == 0) {
      if (exercises[i][0] * 100 > 0) {
        exercises[i][1] += 2;
      }
      else if (exercises[i][0] == 0) {
        exercises[i][1] += 2000;
      }
    }
    exercises[i][2] = 0;
  }

  //randomizeExercises()
  recal();
  lcd.clear();
  lcd.print("done resetting!");
  //lcd.setCursor(0, 1);
  //lcd.print("press to play");
  play = 1;
  countdownPlayed = 0;
  currentExercise = 0;
  finishedForTheDay = 0;
  delay(2000);
}


void saveData()
{
  int addr = 0;
  for (int ex = 0; ex < numExercises; ex++)
  {
    EEPROM.put(addr, exercises[ex][1]);
    addr = addr + 2;
    EEPROM.put(addr, exercises[ex][3]);
    addr = addr + 2;
    EEPROM.put(addr, exercises[ex][4]);
    addr = addr + 2;
  }
}

void getData()
{
  int addr = 0;
  for (int ex = 0; ex < numExercises; ex++)
  {
    EEPROM.get(addr, exercises[ex][1]);
    addr = addr + 2;
    EEPROM.get(addr, exercises[ex][3]);
    addr = addr + 2;
    EEPROM.get(addr, exercises[ex][4]);
    addr = addr + 2;
  }
}

/*************************************************
 * Public Constants
 *************************************************/

#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


]]>
https://courses.ideate.cmu.edu/60-223/s2018/work/fitbox-by-enriques-gizmos-final-documentation/feed/ 0