Introduction

We created an assistive device for an older adult that would improve a certain aspect of their daily routine or bring more positivity to their life. We collaborated with our Osher client, Fredrick, and communicated with him throughout the process of the project to help us figure out the design for our intervention. The problem we decided to tackle was how we could make Fredrick’s daily exercise routine more interesting by creating a guide and friend that could not only carry the routine sequence but make the process more engaging.

What We Built

Our device is a hand-sized interactive device that will act as your exercise guide and companion that will shuffle your exercise routine and encourage you to finish each session. The device will say the exercise title through speakers and it will also display the exercise title on its screen in the front. You communicate to the device when you have completed that exercise by making a loud sound (this means that the environment you are working out in should not have too many loud noises or disturbances). The device will track your overall progress with the exercise routines and at the very end, it will congratulate you that you have accomplished all the exercises.

Final Product

Our Exercise Companion device

How the device lights up

Where to change the batteries

The rotary encoder allows you to switch to a different set of routines and shuffle routine

Simulation of a button press to shuffle an exercise routine in a category

Simulation of button rotation to go to next exercise category

 

Animation of how the device works overall

TinkerCad

The main interaction stimulated in TinkerCAD.

The celebration when the workout is finished

Physical Mockups

A prototype of how the device could work

On one of the screens portrays a digital face

Prototype of how the device could look

Interacting with the button press

To learn more about our prototypes, click here 

Narrative Sketch

To map out all the interactions Frederick might have with the exercise companion, we developed storyboards of different use scenarios.

The exercise companion is powered on with a long press of the button. A personalized welcome message is displayed on the LCD screen and is announced on the speaker. The eyes on the device respond by blinking.

The user can move on to the next exercise by pressing the button once. The name of the exercise is announced on the speaker and LED lights of the progress bar indicates the progress through the exercise session.

Alternatively, the user can also skip to the next exercise with an audio input by saying “next” out loud.

The exercise companion celebrates with an audio and a visual message when the exercise session is complete and the lights on the progress bar are all lit up.

The user can switch between different exercise routines by turning the rotary dial on the exercise companion.

How We Got Here

We initially interviewed Fredrick and brainstormed multiple ideas with him.  In the end, we decided to pursue the exercise companion because it was the most feasible, and Fredrick also agreed that it would be something fun and useful for him.

Our Zoom interview with Fredrick

A sample of our interview

To see our full interview story click here

The basic concept was to have a device that would shuffle his exercise routines. He would press the button to shuffle the exercise routine and the device would countdown each exercise for Fredrick to follow along. There would also be LED lights to track the overall progression of the routine.  But as we got into the specifics, we changed and developed our device to better solve the problem:

A collaborative drawing done by all team members over Zoom. This incorporated all of our individual prototype designs.

  1. Adding voice feedback

The first time we revealed to Fredrick what we were making, we had a button that he would be able to press every time he completed a workout. However, he raised the concern that it would be very cumbersome to get up to press the button every time, therefore, this was when we decided to use sound to go to the next workout.

Initially, we had a button but changed it to a rotary encoder to better fit Fredrick’s needs. The voice recognition would be inside the device near the speaker holes.

2. Instead of two touch screen faces on opposite ends, we used a 16×2 LCD screen

In our first final prototype, we decided to have a cubic screen with a face that acts as the “personality” of the device and changed according to the progression and accomplishment of the exercises. Another screen was in the back to have the title of the routines, in case Fredrick did not hear the exercises being called out through the speakers. However, we realized this was a little unnecessary and would add bulk to our physical size, therefore, we simplified this concept by using the form of the 16×2 LCD screen as the mouth of the device. The eyes of the mouth would be cut out from the body of the device and placed  with acrylic on top to be able to see LED lights inside. The LED lights in the eyes would then flash to animate the face.

Initial rendering of the device with two cubic screens, one in front and one in the back

Final design of device with one side of the cube with a face made from acrylic, a 16×2 LCD screen, and a LED strip progression bar. The back is empty.

The face is now constructed by essential functions of the device

3.  Adding multiple routines with a rotary encoder

We realized if we shuffled all the workout exercises, it would be difficult changing back and forth from a  standing position to a floor exercise. Therefore, this is when we incorporated workout categories for Fredrick’s exercises. Instead of shuffling all the exercises, we would shuffle the exercises in a category. Thus we replaced the button we planned to use before, to a rotary encoder, where the dial part of the rotary encoder would be able to switch between categories, and the button is able to shuffle the exercises in that category.

Using a rotary encoder instead of a button

4. Congratulatory  responses

As we developed our idea of the exercise companion, we wanted to animate the device, even more, to give it more personality. Therefore, we added features where the device would congratulate you at the end of the workout and play a song that you can choose. This detail we hope would allow the user to enjoy the device more.

To see this please look at the video of the interaction under the section, Final Product.

Conclusions and Lessons Learned

During our presentation, one of the verbal comments was that the device had “an infantile appeal” to it.  We thought this was very interesting where we did not mean to make it appear like that at all, in fact, we tried to create the device so that it would appear as a neutral face. Perhaps to make it more friendly, we could animate the expressions and have them more lifelike that way. Looking at our written feedback we also received feedback that stated our device’s appearance was “cute” and they “love the look”. This shows us that our device may have successfully taken a neutral ambiguous look and that is why people are having different reactions to it – it is up to the user to determine the mood of the face. For our case, our user is Fredrick and during our meetings with him to show our device, he enjoyed the appearance. Moving forward, it may be better to include more “faces” to make the device more lively, as pointed out in one of the comments. One of the biggest limitations, however, comes from the LCD display, which has limited ability to display graphics or animation.

In our written comments, more than one person wrote that they were afraid that the user may “game the system” by actively skipping over the exercise right away.  This may be the case, but for our client’s specific situation, he did his exercises daily anyways and only needed this tool to make his routines more interesting. The device is not meant to push him to do the exercises, therefore, this would not have been a problem. If we were to design something for the purpose of helping users exercise, then we could include a mandatory timer for each exercise.

Another comment we received was that “to display the time remaining”. We initially had this feature, but our client declined it as he told us that he wanted to take charge of how long he would spend doing an exercise. He did not want the device to rush him. We think through this project, we learned that designing a device for a one person’s needs makes the device’s functions very specific, therefore, when other’s try to imagine using it, they might not always realize the background information behind it and will not be able to relate to the device as much.

Working remotely was a new experience for all of us, and although it becomes more time-efficient without the scheduling of physical meetings and the making, communicating was more difficult. It was not always very clear what each person was doing and the pace of work was difficult to track as well. The Gantt sheet was good to help us schedule what we needed to accomplish, but what was most effective was actually meeting through the Zoom meetings. Since members of the group did not meet, accountability for accomplishing work was also very individualized, which can be difficult for people facing different situations. This experience made us realize how important physical meetings were. On the other hand, working remotely has turned out to be a better experience than we expected. Even though we cannot have in-person interaction which is important for any activity, we can still communicate effectively through technology if we have the resolution. For example, brainstorming as a group may become harder or different, but with the whiteboard feature on Zoom, it is actually quite easy and engaging.

We really enjoyed talking with the Osher students and making these interesting devices where we didn’t need to think about the cases for multiple users and just one. We hope to take our plans for this project and actually create it in real life sometime in the future and finally be able to hold our hard work in our hands.

Our biggest takeaway from this project would be seeing how important the research and communication with the users are. By talking and understanding our client, Fredrick, our design was changed and guided by his needs. Without this information, the device would not have been as useful and meaningful as it is. This would apply to our future projects as well where we want to focus more time on planning and building a strong foundation of study and research for a design project that I am doing. In addition, we have learned that everyone has their own standard of what a good device or product should be. In the process of designing, we start by envisioning the device in our perspectives. Then it is crucial to communicate with the actual users to find what is working and what is off. Oftentimes there may be features that the designers thought is important but not so by the users, or features that the users value but are not considered by the designers.

Technical Details

Breadboard Image

This is the annotated breadboard image

Code

/*Exercise Companion
 * Zerui Huo
 * The code makes a robot-shaped device show the exericise names through the selected workout set. It goes to the next execise when it detects sound that has volume
   over the threshold. It also shows reward when the user finishes a group of exercises or the whole set. A newpixel strip shows the progress through the workout.

 *Pin mapping:
  
     pin   | mode   | description
     ------|--------|------------
     6      INPUT     This pin represents the press button on a rotary encoder.
     7      OUTPUT    The pin that controls the Neopixel ligt strip/stick
     8      INPUT     The pin that connects to the microphone that sense any audio input from the user.
     10     OUTPUT    The pin that controls one of the two speakers.
     13     OUTPUT    The pin that controls one of the two speakers.
     A0     INPUT     The pin that represents the rotary turner on the a rotary encoder.
     A3     OUTPUT    Used as digital pin to control LEDS light.
     A4     OUTPUT    Used as digital pin to control LEDS light.
     VCC              The pin for power to go into the Arduino pro mini.Connected to a 9V battery.
     

 *Credit to example codes or reference:
  *The code that generates the custom characters in LCD is written in the help of the website https://maxpromer.github.io/LCD-Character-Creator/.
  *The code for the neopixel light strip is written by refering to the information in the website https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-use. It is a turtorial on how to use the neopixel library.
  

 *Note: In each workout set, exercises requiring similar pose are put into groups. Tree pose, for example, is grouped with other yoga exericise with standing pose.
        The workout sets always have the same groups of exercises,but the sequence of the groups is randomized every time. In addition, the sequence for exerices in
        each group is also randomized.
*/


//Included the needed Libraries
#include <Wire.h>
#include <LiquidCrystal.h>
#include <Adafruit_NeoPixel.h>

//Defining the "audio files" for the exerice names
#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



//Naming all the pins according to stimulation in TinkerCad
//Remember to change the pin according to your wiring
const int ROTARY_BUTTON = 6;
const int ROTARY_TURNER = A0;
const int MICROPHONE = 8;
const int SPEAKER1 = 10;
const int SPEAKER2 = 13;
const int NEOPIXELSTRIP = 7;
const int LED1 = A3;
const int LED2 = A4;

//Setting the constant ints. Fre stands for the frequency to check a specific timer. Period is the period for some events to last.
const int LED_COUNT = 6;
const int SELECTINGFRE = 100;
const int MICROPHONEFRE = 20;
const int AUDIOPERIOD = 60;
const int STRIPPERIOD = 100;

//Global varibales that change
//Timers for "run without block"
unsigned long selectingTimer = 0;
unsigned long microphoneTimer = 0;
unsigned long audioTimer = 0;
unsigned long stripTimer = 0;

int on_off = 1;
int select = 1;
int cursor = 0;
int row;
int column = 0;
int rowIncrement = 0;
int columnIncrement = 0;
int num_of_groups;

//Lists that store informations
//The set names
String sets[] = {"Yoga Set", "Weights", "Aerobics"};

//The list that tells the row number that a set starts and ends
int set_row[] = {0, 2, 4, 8};

//Example workout sets. The "0" represents a null, and the device will go to the next group of exercises in the set.
String exercise[][3] = {
  {"Jumping Jack Jumping Jack Jumping Jack", "Cobbler Pose", "Abdominal Plank"},
  {"hee", "ha", "yo"},
  {"Tree Pose", "dance", "Upward Facing Dog"},
  {"Cat-cow", "Knee-to-chest", "0"},
  {"Knees-to-chest", "Hip flexor", "Torso rotation"},
  {"Tree pose", "Full body", "Child's Pose"},
  {"Cat-cow", "Crescent warrior", "0"},
  {"Ab plank", "Cobbler Pose", "0"},
};

//Micmic the SD card that stores the audio files of the names of the exercise
int exercise_audio[][3] = {
  {NOTE_C3, NOTE_D3, NOTE_E3},
  {NOTE_F3, NOTE_G3, NOTE_A3},
  {NOTE_B3, NOTE_C4,  NOTE_D4},
  {NOTE_E4, NOTE_F4, NOTE_F5},
  {NOTE_G4, NOTE_A4,NOTE_B4},
  {NOTE_C5, NOTE_D5, NOTE_E5},
  {NOTE_F5, NOTE_G5,  NOTE_A5},
  {NOTE_B5, NOTE_C6, NOTE_D6},
};

//Creating the custom characters for the "face" of the robot
//surprise face
byte SURPRISE_06[] = {
  B00000,
  B00001,
  B00010,
  B00100,
  B01000,
  B01000,
  B01000,
  B01000
};

byte SURPRISE_07[] = {
  B01111,
  B10000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

byte SURPRISE_08[] = {
  B11110,
  B00001,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

byte SURPRISE_09[] = {
  B00000,
  B10000,
  B01000,
  B00100,
  B00010,
  B00010,
  B00010,
  B00010
};

byte SURPRISE_16[] = {
  B01000,
  B01000,
  B01000,
  B01000,
  B00100,
  B00010,
  B00001,
  B00000
};


byte SURPRISE_17[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B10000,
  B01111
};

byte SURPRISE_18[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00001,
  B11110
};

byte SURPRISE_19[] = {
  B00010,
  B00010,
  B00010,
  B00010,
  B00100,
  B01000,
  B10000,
  B00000
};

//smiley face
byte SMILE_04[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00001,
  B00010,
  B00100
};

byte SMILE_05[] = {
  B00000,
  B00000,
  B00000,
  B01000,
  B11000,
  B00100,
  B00010,
  B00001
};


byte SMILE_010[] = {
  B00000,
  B00000,
  B00000,
  B00010,
  B00010,
  B00101,
  B01000,
  B10000
};

byte SMILE_011[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B10000,
  B01000
};

byte SMILE_16[] = {
  B10000,
  B01000,
  B00100,
  B00010,
  B00001,
  B00000,
  B00000,
  B00000
};

byte SMILE_17[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B10000,
  B01111,
  B00000
};

byte  SMILE_18[] = {
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00001,
  B11110,
  B00000
};


byte SMILE_19[] = {
  B00001,
  B00010,
  B00100,
  B01000,
  B10000,
  B00000,
  B00000,
  B00000
};




// generating a random sequence for exerices in each exercise group
int * generate_randnumb_exercise(int min, int length) {
  static int rando[10];
  for (int i = min; i < min + length; i++) {
    rando[i - min] = i;
  }



  randomSeed(digitalRead(A5));
  for (int i = 0; i < 200; i++) {
    int rand1;
    int rand2;
    int old_rand1;
    int old_rand2;
    rand1 = random(0, length - 1);
    rand2 = random(0, length - 1);
    old_rand1 = rando[rand1];
    old_rand2 = rando[rand2];
    rando[rand1] = old_rand2;
    rando[rand2] = old_rand1;
  }
  return rando;
}

// generating a random sequence for exerice group in set1
int * generate_randnumb_set1(int min, int length) {
  static int rando2[10];
  for (int i = min; i < min + length; i++) {
    rando2[i - min] = i;
  }


  randomSeed(digitalRead(A5));
  for (int i = 0; i < 200; i++) {
    int rand1;
    int rand2;
    int old_rand1;
    int old_rand2;
    rand1 = random(0, length - 1);
    rand2 = random(0, length - 1);
    old_rand1 = rando2[rand1];
    old_rand2 = rando2[rand2];
    rando2[rand1] = old_rand2;
    rando2[rand2] = old_rand1;
  }
  return rando2;
}


// generating a random sequence for exerice group in set2
int * generate_randnumb_set2(int min, int length) {
  static int rando3[10];
  for (int i = min; i < min + length; i++) {
    rando3[i - min] = i;
  }



  randomSeed(digitalRead(A5));
  for (int i = 0; i < 200; i++) {
    int rand1;
    int rand2;
    int old_rand1;
    int old_rand2;
    rand1 = random(0, length - 1);
    rand2 = random(0, length - 1);
    old_rand1 = rando3[rand1];
    old_rand2 = rando3[rand2];
    rando3[rand1] = old_rand2;
    rando3[rand2] = old_rand1;
  }
  return rando3;
}


// generating a random sequence for exerice group in set3
int * generate_randnumb_set3(int min, int length) {
  static int rando4[10];
  for (int i = min; i < min + length; i++) {
    rando4[i - min] = i;
  }



  randomSeed(digitalRead(A5));
  for (int i = 0; i < 200; i++) {
    int rand1;
    int rand2;
    int old_rand1;
    int old_rand2;
    rand1 = random(0, length - 1);
    rand2 = random(0, length - 1);
    old_rand1 = rando4[rand1];
    old_rand2 = rando4[rand2];
    rando4[rand1] = old_rand2;
    rando4[rand2] = old_rand1;
  }
  return rando4;
}

//Pointers that refers to the sequence of exercise/exercise groups
int *sequenceExercise = generate_randnumb_exercise(0, 3);
int *sequenceSet1 = generate_randnumb_set1(set_row[0], set_row[1] - set_row[0]);
int *sequenceSet2 = generate_randnumb_set2(set_row[1], set_row[2] - set_row[1]);
int *sequenceSet3 = generate_randnumb_set3(set_row[2], set_row[3] - set_row[2]);

//set up of the lcd
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//set up the neopixel strip
Adafruit_NeoPixel strip(LED_COUNT, NEOPIXELSTRIP, NEO_GRB + NEO_KHZ800);
//defining the color for the neopixel strip
uint32_t deep_sky_blue = strip.Color(0, 191, 255);


//Supplemental Functions
//Functions for selecting a workout set
//Determine which set the rotary turner is "pointing at"
int which_set () {
  int error_angle = 30;
  int potenValue = analogRead(ROTARY_TURNER);
  int set = 0;

  //Display the name of set 1 when the potentiometer is at 0 degree
  if (potenValue <= 1023 * error_angle / 300) {
    set = 1;
    return set;
  }


  else if (potenValue >= 512 - 1023 * error_angle / 300 && potenValue <= 512 + 1023 * error_angle / 300) {
    set = 2;
    return set;
  }

  //set 3
  else if (potenValue >= 1023 * (1 - 1 * error_angle / 300)) {
    set = 3;
    return set;
  }
}

//Display the name of the workout set that is currently looking at
void select_set() {
  int buttonState = digitalRead(ROTARY_BUTTON);
  int set;
  set = which_set();
  String text = sets[set - 1];
  int startingColumn = ceil((16 - text.length()) / 2);

  lcd.setCursor(2, 0);
  lcd.print("Select a Set");


  if (on_off == 1) {
    lcd.setCursor(startingColumn, 1);
    lcd.print(text);
  }
  if (on_off == 0) {
    lcd.setCursor(startingColumn, 1);
    lcd.print("               ");
  }

  //When the pressbutton on rotary encoder is pressed
  if (buttonState == LOW) {
    select = 0;

    if (set == 1) {
      row = *sequenceSet1;
      num_of_groups = set_row[1]-set_row[0];
      Serial.println(row);
    }
    if (set == 2) {
      row = *sequenceSet2;
      num_of_groups = set_row[2]-set_row[1];
      Serial.println(row);
    }
    if (set == 3) {
      row = *sequenceSet3;
      num_of_groups = set_row[3]-set_row[2];
      Serial.println(row);
    }

    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("Lets Begin!");
    delay(1500);
    lcd.clear();
    //Call the function robot_face to show a smiley face on the lcd
    robot_face("smiley");
    delay(2000);
    lcd.clear();
    audioTimer = millis();
  }
}

//Sumplementary function for display exercise name onto the LCD display
//This function scroll the text when it is too long to display on the 16-digit screen
void scrollLeft(String string, int row) {
  int length = string.length();
  if (millis() - selectingTimer > SELECTINGFRE) {
    if (cursor + 1 <= length ) {
      cursor = cursor + 1;
    }
    if (cursor + 1 > length) {
      cursor = 0;
    }
    selectingTimer = millis();
    lcd.clear();
  }
  String text = string.substring(cursor);
  lcd.setCursor(0, row);
  lcd.print(text);
}

//Function that show the name of the current exercise
void display_text(String string) {
  int length = string.length();
  if (length <= 16) {
    lcd.setCursor(ceil((16 - length) / 2), 0);
    lcd.print(string);
  }

  if (length > 16 && length < 32) {
    int indexofSpace = string.indexOf(" ");
    String firstWord = string.substring(0, indexofSpace);
    String secondWord = string.substring(indexofSpace + 1);

    if (firstWord.length() > 16) {
      scrollLeft(firstWord, 0);
    }
    if (firstWord.length() <= 16) {
      lcd.setCursor(ceil((16 - firstWord.length()) / 2), 0);
      lcd.print(firstWord);
    }

    if (secondWord.length() > 16) {
      scrollLeft(secondWord, 1);
    }
    if (secondWord.length() <= 16) {
      lcd.setCursor(ceil((16 - secondWord.length()) / 2), 1);
      lcd.print(secondWord);
    }
  }
  if (length >= 32) {
    scrollLeft(string, 1);
  }
  
  if (string == "end") {
    lcd.setCursor(ceil((16 - 11) / 2), 0);
    lcd.print("The workout");
    lcd.setCursor(ceil((16 - 11) / 2), 1);
    lcd.print("is finished");
  }
}

//This micmics the function of the mini mp3 player, which plays a audio record of the name of the exercise.
void play_audio(int audio) {
  while (millis() - audioTimer < AUDIOPERIOD) {
  tone(SPEAKER1,audio);
  tone(SPEAKER2,audio);
  }
  noTone(SPEAKER1);
  noTone(SPEAKER2);
}

//Rolling the light on the Neopixel light strip as a reward when a exercise group is finished
void light_up_strip() {
  while (millis() - stripTimer < STRIPPERIOD) {
  int redColor = random(0, 255);
  int greenColor = random(0,255);
  int blueColor = random(0, 255);
  for (int i=0;i < 6; i++) {
  strip.setPixelColor(i,strip.Color(redColor,greenColor,blueColor));
  }
  strip.show();
  }
  strip.clear();
}

//That function that put the custom characters onto the lcd
void robot_face(String face) {
  if (face == "surprise") {
     //Create the custom characters
     lcd.createChar(0, SURPRISE_06);
     lcd.createChar(1, SURPRISE_07);
     lcd.createChar(2, SURPRISE_08);
     lcd.createChar(3, SURPRISE_09);
     lcd.createChar(4, SURPRISE_16);
     lcd.createChar(5, SURPRISE_17);
     lcd.createChar(6, SURPRISE_18);
     lcd.createChar(7, SURPRISE_19);

     //write the characters to their places
     lcd.setCursor(6, 0);
     lcd.write(byte(0));
     lcd.setCursor(7, 0);
     lcd.write(1);
     lcd.setCursor(8, 0);
     lcd.write(2);
     lcd.setCursor(9, 0);
     lcd.write(3);
      lcd.setCursor(6, 1);
     lcd.write(4);
     lcd.setCursor(7, 1);
     lcd.write(5);
     lcd.setCursor(8, 1);
     lcd.write(6);
     lcd.setCursor(9, 1);
     lcd.write(7);
  }

   if (face == "smiley") {
     //Create the custom characters
     lcd.createChar(0,  SMILE_04);
     lcd.createChar(1,  SMILE_05);
     lcd.createChar(2,  SMILE_010);
     lcd.createChar(3,  SMILE_011);
     lcd.createChar(4,  SMILE_16);
     lcd.createChar(5,  SMILE_17);
     lcd.createChar(6,  SMILE_18);
     lcd.createChar(7,  SMILE_19);
    
     //write the characters to their places
     lcd.setCursor(4, 0);
     lcd.write(byte(0));
     lcd.setCursor(5, 0);
     lcd.write(1);
     lcd.setCursor(10, 0);
     lcd.write(2);
     lcd.setCursor(11, 0);
     lcd.write(3);
     lcd.setCursor(6, 1);
     lcd.write(4);
     lcd.setCursor(7, 1);
     lcd.write(5);
     lcd.setCursor(8, 1);
     lcd.write(6);
     lcd.setCursor(9, 1);
     lcd.write(7);
  }
}

//Simple function that turn the led on and off
void ledSwith(String command) {
  if (command == "on"){
    digitalWrite(LED1,HIGH);
    digitalWrite(LED2,HIGH);
  }
  if (command == "off"){
    digitalWrite(LED1,LOW);
    digitalWrite(LED2,LOW);
  }
}

//The function that determines what happens when the workout set is finished
void end() {
  display_text("end");
  delay(2000);
  lcd.clear();
  display_text("Great Job!");
  delay(2000);
  lcd.clear();
  robot_face("smiley");
  int redColor = random(0, 255);
  int greenColor = random(0,255);
  int blueColor = random(0, 255);
  
  //The for loop rolls the lgiht on the LED light strip until the device is powered off
  for (int i=0;i < 6; i++) {
  strip.setPixelColor(i,strip.Color(redColor,greenColor,blueColor));
  strip.show(); 
  delay(400);

    if (i==5) {
      i = -1;
      redColor = random(0, 255);
      greenColor = random(0,255);
      blueColor = random(0, 255);
    }
  }
}



void show_exercise() {
  //Displaying the name of the exercise by feeding the word to function display_text()

  
  //If the selected workout set is set 1
  if (row < set_row[1]) {
    row = *(sequenceSet1 + rowIncrement);
    column = *(sequenceExercise + columnIncrement);
   

    // columnIncrement in  the if-statement should be set to the number of columns in each row
    //The if statement checks if the exercise group is finished
    if (exercise[row][column] == "0" || columnIncrement == 3) {
      rowIncrement = rowIncrement + 1;
      columnIncrement = 0;
      row = *(sequenceSet1 + rowIncrement);
      column = *(sequenceExercise + columnIncrement);

      ledSwith("on");
      robot_face("surprise");
      light_up_strip();
      lcd.clear();
      ledSwith("off");
      audioTimer = millis();
      
      //to check if the set is finished
      if (rowIncrement == set_row[1]) {
        end();
      }
    }
    display_text(exercise[row][column]);
    play_audio(exercise_audio[row][column]);
    
  }

  //If the selected workout set is set 2
  if (row >= set_row[1] && row < set_row[2]) {
    row = *(sequenceSet2 + rowIncrement);
    column = *(sequenceExercise + columnIncrement);
    

    // columnIncrement in  the if-statement should be set to the number of columns in each row
    if (exercise[row][column] == "0" || columnIncrement == 3) {
   
      rowIncrement = rowIncrement + 1;
      columnIncrement = 0;
      row = *(sequenceSet2 + rowIncrement);
      column = *(sequenceExercise + columnIncrement);
      
      ledSwith("on");
      robot_face("surprise");
      light_up_strip();
      lcd.clear();
      ledSwith("off");
      audioTimer = millis();
        
      //to check if the set is finished
      if (rowIncrement == set_row[2] - set_row[1]) {
        end();
      }
    }
    display_text(exercise[row][column]);
    play_audio(exercise_audio[row][column]);
    
  }

  //If the selected workout set is set 3
  if (row >= set_row[2] && row < set_row[3]) {
    row = *(sequenceSet3 + rowIncrement);
    column = *(sequenceExercise + columnIncrement);
   

    // columnIncrement in  the if-statement should be set to the number of columns in each row
    if (exercise[row][column] == "0" || columnIncrement == 3) {

      rowIncrement = rowIncrement + 1;
      columnIncrement = 0;
      row = *(sequenceSet3 + rowIncrement);
      column = *(sequenceExercise + columnIncrement);
      
      robot_face("surprise");
      ledSwith("on");
      light_up_strip();
      lcd.clear();
      ledSwith("off");
      audioTimer = millis();

      
      //to check if the set is finished
      if (rowIncrement == set_row[3] - set_row[2]) {
        end();
      }
    }
    display_text(exercise[row][column]);
    play_audio(exercise_audio[row][column]);
  }
}

//This funtion controls the neopixel strip to show the progress through the set
void show_progress(int num_of_groups) {
  int lightedPixel = (rowIncrement*6)/num_of_groups;
  
  for (int i=0;i < lightedPixel; i++) {
  strip.setPixelColor(i,deep_sky_blue);
  }
  strip.show();
}


void setup() {
  // Setting up the pins
  pinMode(ROTARY_BUTTON, INPUT_PULLUP);
  pinMode(ROTARY_TURNER, INPUT);
  pinMode(SPEAKER1, OUTPUT);
  pinMode(SPEAKER2, OUTPUT);
  pinMode(MICROPHONE,INPUT_PULLUP);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);

  //Setting up for the lcd screen
  lcd.begin(16, 2);
  lcd.setCursor(2, 0);
  lcd.print("Lets Exercise!");
  delay(1500);
  lcd.clear();

  //Initiate the Neopixel Strip
  strip.begin();
  strip.show();


  //Initiate Serial Print
  Serial.begin(9600);
}

void loop() {
  while (select == 1) {
    select_set();
    //Timer to make the chosen workout set "flash" 
    if (millis() - selectingTimer > SELECTINGFRE) {
      if (on_off == 1) {
        on_off = 0;
      }
      else {
        on_off = 1;
      }
      selectingTimer = millis();
    }
  }

  if (millis() - microphoneTimer > MICROPHONEFRE) {
    int buttonState = digitalRead(MICROPHONE);
    if (buttonState == LOW) {
      audioTimer = millis();
      stripTimer = millis();
      columnIncrement = columnIncrement + 1;
      lcd.clear();
    }
    microphoneTimer = millis();
  }
  show_exercise();
  show_progress(num_of_groups);
}

Schematic and Design Files

Schematic

Exercise Companion.stl