Allie's Affirmation Dog

By Team Walnuts: Andreas, Tippi, Myles 

Final Documentation


The device is a cubic shape that resembles a dog head. The head of the dog is pink, the ears are green, and the neck is blue. The left ear is labelled PRINT and the right ear is labeled CONTINUE; both have buttons on top. There is an LCD screen in place of the dog's eyes and a joystick in place of its nose. A piece of receipt paper is coming out of the dog's mouth in a similar fashion to a tongue.

A three-quarters view of the device. The power switch on the side is in the off position and the LCD screen is off.

Our project, Allie's Affirmation Dog, is a helpful reminder tool for our client Allie to stay cheery, upbeat, and positive throughout the day.  For our Physical Computing final project were tasked with designing an assistive device for a person living with a disability.  We were assigned Allie as our client---a Pittsburgh native who loves dogs, singing, and movies.  Allie is a wheelchair user and consequentially some day-to-day tasks can be difficult for her, however most of these issues were out of scope for our expertise and timeframe. She also told us that she struggles with self esteem and positivity throughout the day. During our interview, which you can find more info on here, she asked for use to create a device that could give her words of encouragement at different times in her day. The device, either after being prompted by Allie or at a random time, will give her a positive affirmation, which she has the option to print and keep for later. Since Allie's favorite animals are dogs, our team decided to make the machine in the form of techno-inspired dog. 

The device is in the same position as the above image, but the LCD screen is on and displaying the image of two square eyes made up of Unicode characters.

An overall view of the device in its idle state.

The object is resting in a very similar position as the previous pictures except the left face is shown instead of the right, obscuring the power switch. The tongue/receipt is extending much farther out of the mouth. There is also an Arduino USB cable wrapped around the dog's ears for scale.

An overall view of the object resting on a table in IDeATe for scale.

This is a close up picture of the LCD screen part of the face. The eyes are comprised of 7x4 block squares with a couple blocks missing to create the impression of a glint in the eyes.

A close up view of the eyes in the idle state.

This picture shows the LCD screen once again. The eyes in this picture are downward facing semi-circles, making a cartoonish image of the dog closing its eyes very happily.

A close up view of the eyes when the continue button is pressed.

Walnuts Demo.mov

This video demonstrates the process of turning on the device, prompting an affirmation, and printing that affirmation. First, the user turns on the maching by flipping the power switch. Then the dog's eyes light up and go into idle mode. After that, the user prompts an affirmation by moving the joystick on the dog's nose (either ear button may also be pressed) and an affirmation appears on the LCD screen in place of the eyes. After that, the user presses the left ear button, labeled "PRINT". The tongue of the dog then begins to extend further out as the receipt is printed. When it is done, the user tears off the receipt and shows it to the camera.

Process

After the first interview with our client Allie, we settled on the idea of building a positive affirmation machine to make her happy throughout the day.

Here are sketches of the appearance of the machine, electric components needed, workflow chart as well as box diagram.


From sketching the idea, we have several questions we would like to solve from making prototypes and getting feedback from our client.

Question 1: What size and shape is the most aesthetically pleasing while also being practical for use and housing all necessary components?

Prototype1: made with cardboard, aiming to be aesthetically pleasing with a playful design and have the appropriate size to fit all the electronic components. 

Question 2:What is best way to attach the device the clients tray?


Prototype 2: This laser cut clamp is our original solution to attach the machine to our client's tray.

Question 3: What modes of output and types of affirmation are more preferable?


Prototype 3: experimentation with different forms of output from the machine. We did visual display with receipt printer and LCD screen as well as sound output with DFPlayer.

After the second interview with our client Allie where we showcased our prototypes, we have a clearer direction for our project with regards to the size, height and appearance of the machine, accessibility of the buttons and receipt printer as well as the content and modes of outputs

Notes from the second interview ^^

The working electronics with code.

We worked on the display for the eye-mode by making our custom pixel and drawing eyes with the pixels.

Also, we used a joystick as a button for our core functionality, affirmation generation, since it is bigger than a normal button and easy to control. 

We used more durable stranded wires secured with crimps on both ends for the final product

Here are the soldered components. 

Experimentation with different parameters of the receipt printer to get more legible thermal prints .

Experimentation with patterns on empty areas of the receipt, making it more tongue-like.

For our Dog Model we custom designed the form in Rhino and Generated finger joints using Grasshopper

To verify our model accuracy we did a test cut out of black acrylic. We did not nail it on the first. The ears were too small and our button design in the ear did not work. 

Over several iterations we  had issues with the hatching of different parts on laser cut components. Even with the same settings, same color hatch image and flat material, the hatch intensity varied

We  made several iterations to configure our custom ear button to line up correctly with our internal microswitch

IMG_6582.MOV

After several iterations, we got the button up and running to respond to pressure from different locations and fit with our laser cut ear design.

Final step: putting the lasercut box and electronic components together! 

Closeup shoots of the inner organization: screwed in lcd screen and a lego block to elevate and support the joystick button.

Final touch: Soldering the on/off button and adding tape to isolate the exposed wire crimps from each other.

Discussion

During our final presentation, we received several good comments, suggestions, and critiques. However, two main comments stood out. The first was the ability of this project to apply and assist all people. Several reviewers said they could “see this machine as a toy in Walmart or Target.” One reviewer, in particular, raved about how they could see their child playing with this toy throughout the day and its potential for the design to be configured into other forms like cats and flowers. The ability of the positivity machine to be applied to a general mass consumer market stood out in comparison to the comments of one of the emerita design professors. Her comments leaned toward personalizing the positivity machine to Allie’s specific needs. She emphasized that we could take this project to another level by having Allie generate a list of positive affirmations that she liked. By incorporating Allie into the affirmation-generation process, we could expand beyond the generic affirmations that we chose to include positive song lyrics from her favorite songs, words of wisdom, or even nice quotes from her friends and family. These comments in addition to the incorporation of more dog themed controls like pet instead of continue or good boy instead of print could have made the device more personal. Although these main reviewer comments differ in their suggestions, they both align with the goal of our project: to act as a playful, engaging, and positive tool for daily motivation.

Looking back on our final project, there are several things we would do differently. Regarding the fabrication and internal make-up of our positivity machine, one change we would make is to create specific spots and places for each internal hardware component of the machine. Secondly, the access hatch on the back of our machine flips the wrong way: instead of flipping down like a drawbridge, it flips up like a garage door. Switching this behavior would make it easier for any user to change the batteries of our machine and improve the hardware and connections. Another self-critique of the project is the use of buffers to prevent unwanted electrical contact between components. We spent hours debugging unexpected behavior due to solderless connections accidentally touching. Though we alleviated these issues through the use of an Arduino adapter, we could have also used solderable Arduino boards. Another feature we thought we could have added was to have the Allie's dog make noises, like barking or playing audio recordings of positive affirmations.  Lastly, it would have been nice to have a fully pink machine for Allie, since Allie’s favorite color is pink. It would also have been nice to have Velcro present at the final review so Allie could secure the machine to her chair and tray. 

Despite these minor fixes, our team is very happy with our final product and its ability to generate reminders to stay positive throughout the day.  Looking back our would remind ourselves, to a lot more time  for laser cutting, assembling, and most importantly debugging during the build project, and most importantly, to stay positive.

=

Technical Information

Code

/*

Final Project: Positive Affirmation Machine


Team Name: Team Walnuts

Team Members: Andreas, Myles, Tippi

Client: Allie from CLASS


Purpose: To help Allie throughout her day by delivering positive affirmations

         at random intervals or upon request. Modeled to look like a dog head.


Pin mapping:


Arduino pin | role  | details

------------------------------

2             input   Left ear button

5             input   Right ear button

8             output  Arduino tx -> printer rx

9             input   printer tx -> Arduino rx

A0            input   Joystick x axis

A1            input   Joystick y axis



Released to the public domain by the authors, December 2024

Andreas Wieslander, awieslan@andrew.cmu.edu

Tippi Li, yushuang@andrew.cmu.edu

Myles Sampson, mbsampso@andrew.cmu.edu

*/


#include <SPI.h>  //including libraries

#include <SD.h>

#include <string.h>

#include "Adafruit_Thermal.h"

#include "SoftwareSerial.h"

#include <LiquidCrystal_I2C.h>


//PINS

const int LEFTEARPIN = 2;

const int RIGHTEARPIN = 5;

const int JOYXPIN = A0;

const int JOYYPIN = A1;

#define TX_PIN 8  // Arduino transmit  YELLOW WIRE  labeled RX on printer

#define RX_PIN 9  // Arduino receive   GREEN WIRE   labeled TX on printer


unsigned int timer = 0;  //used to keep track of random intervals for affirmation

unsigned int delayLength = 0;

const int MAXDELAY = 10000;  //in milleseconds

const int MINDELAY = 5000;


int currentState = 0;  //0 means idle, 1 means active


File affirmations;  //.txt file containing all affirmations in a list


//Thermal Printer

SoftwareSerial mySerial(RX_PIN, TX_PIN);

Adafruit_Thermal printer(&mySerial);


//LCD Screen Display

LiquidCrystal_I2C lcd(0x27, 20, 4);

// time tracker

unsigned long lastUpdateTime = 0;           // Tracks the last time eyes changed

const unsigned long updateInterval = 1000;  //5 * 60 * 1000; // 5 minutes in milliseconds



// Custom Blocks for LCD Screen Display

byte emptyBlock[8] = {

  B00000,

  B00000,

  B00000,

  B00000,

  B00000,

  B00000,

  B00000,

  B00000

};

byte fullBlock[8] = {

  B11111,

  B11111,

  B11111,

  B11111,

  B11111,

  B11111,

  B11111,

  B11111

};


void setup() {

  pinMode(LEFTEARPIN, INPUT);  //initializing pins

  pinMode(RIGHTEARPIN, INPUT);

  pinMode(JOYXPIN, INPUT);

  pinMode(JOYYPIN, INPUT);


  mySerial.begin(19200);  //NOT debugging, this is for printer communication


  lcd.init();  //initalizing LCD

  lcd.backlight();

  lcd.createChar(0, fullBlock);

  lcd.createChar(1, emptyBlock);

  openEyes();


  randomSeed(analogRead(0));  //initializing random num


  affirmations = SD.open("test.txt");  //retrieving affirmation file

}


void loop() {

  int leftEarPressed = digitalRead(LEFTEARPIN);  //reading inputs

  int rightEarPressed = digitalRead(RIGHTEARPIN);

  int joyX = analogRead(JOYXPIN);

  int joyY = analogRead(JOYYPIN);

  int joyPressed = 0;


  if ((joyX < 400 || joyX > 700) || (joyY < 400 || joyY > 700)) {  //reading joystick; functions as a button

    joyPressed = 1;

  } else joyPressed = 0;


  if (currentState == 0) {   //if idle

    if (delayLength == 0) {  //if code just started

      randomizeDelay();

    } else if (timer >= delayLength) {  //if random interval over

      currentState = 1;                 //go to active

      timer = 0;

      randomizeDelay();

    } else if (leftEarPressed == 1 || rightEarPressed == 1 || joyPressed == 1) {  //if any input pressed

      currentState = 1;                                                           //go to active

      timer = 0;

      randomizeDelay();

    }

  } else if (currentState == 1) {  //if active

    randomSeed(analogRead(0));

    unsigned int affInd = random(0, 100);  //random affirmation index


    String affirmation = getAffirmation(affInd);

    lcd.clear();

    lcd.setCursor(0, 0);

    if (affirmation.length() <= 80) {  //printing to LCD with correct wrapping

      displayText(affirmation);        // If it fits on the screen

    } else {

      scrollText(affirmation);  // If it needs to scroll

    }


    bool resolved = false;

    while (!resolved) {                          //wait for input

      leftEarPressed = digitalRead(LEFTEARPIN);  //read inputs

      rightEarPressed = digitalRead(RIGHTEARPIN);

      joyX = analogRead(JOYXPIN);

      joyY = analogRead(JOYYPIN);

      joyPressed = 0;

      if ((joyX < 400 || joyX > 700) || (joyY < 400 || joyY > 700)) {

        joyPressed = 1;

      } else joyPressed = 0;


      if (leftEarPressed) {  //print button

        printer.begin();     //print affirmation

        delay(100);

        printer.justify('C');

        printer.setSize('M');

        printer.println(affirmation);

        printer.feed(2);


        currentState = 0;  //go to idle

        resolved = true;

      } else if (rightEarPressed) {  //continue button

        updateExpression();          //go to idle

        currentState = 0;

        resolved = true;

      } else if (joyPressed) {  //reshuffle button

        affInd = random(0, 100);

        affirmation = getAffirmation(affInd);  //pick another random affirmation

        lcd.clear();

        lcd.setCursor(0, 0);

        if (affirmation.length() <= 80) {  //printing to LCD with correct wrapping

          displayText(affirmation);        // If it fits on the screen

        } else {

          scrollText(affirmation);  // If it needs to scroll

        }

      }

      delay(250);  //for screen readability

    }

    delay(1000);  //button cooldown incase accidentally held

  }

  timer++;

  delay(1);

} //end of loop


void randomizeDelay() {  //randomizes between min and max

  randomSeed(analogRead(0));

  delayLength = random(MINDELAY, MAXDELAY);

}


String getAffirmation(unsigned int lineNumber) {  //partially used code from user septillion on Arduino forums https://forum.arduino.cc/u/septillion/summary

  affirmations.seek(0);                           //retrieves an affirmation from the .txt file on the SD card using an index

  char cr = 'a';

  for (unsigned int i = 0; i < (lineNumber - 1);) {

    cr = affirmations.read();

    if (cr == 0x0A) {

      i++;

    }

  }  //find correct index


  unsigned int lineLength = 0;


  while (true) {

    cr = affirmations.read();

    lineLength++;

    if (cr == '\n') {

      break;

    }

  }  //find length of line


  affirmations.seek(0);

  for (unsigned int i = 0; i < (lineNumber - 1);) {

    cr = affirmations.read();

    if (cr == '\n') {

      i++;

    }

  }  //find correct index


  char chList[lineLength];

  unsigned int index = 0;


  while (true) {

    cr = affirmations.read();

    chList[index] = cr;

    index++;

    if (cr == '\n') {

      break;

    }

  }

  chList[lineLength - 1] = '\0';

  return chList;

}


void closeEyes() {  //face animation on lcd screen

  for (int i = 0; i < 7; i++) {

    lcd.setCursor(i, 1);

    lcd.write(byte(0));

  }

  for (int i = 13; i < 20; i++) {

    lcd.setCursor(i, 1);

    lcd.write(byte(0));

  }

}


void openEyes() {  //face animation on lcd screen

  for (int j = 0; j < 4; j++) {

    for (int i = 0; i < 7; i++) {

      lcd.setCursor(i, j);

      lcd.write(byte(0));

    }

    for (int i = 13; i < 20; i++) {

      lcd.setCursor(i, j);

      lcd.write(byte(0));

    }

  }

  //watery effect

  lcd.setCursor(1, 1);

  lcd.write(byte(1));

  lcd.setCursor(2, 1);

  lcd.write(byte(1));

  lcd.setCursor(14, 1);

  lcd.write(byte(1));

  lcd.setCursor(15, 1);

  lcd.write(byte(1));

  lcd.setCursor(5, 2);

  lcd.write(byte(1));

  lcd.setCursor(18, 2);

  lcd.write(byte(1));

  delay(800);

  lcd.setCursor(1, 1);

  lcd.write(byte(0));

  lcd.setCursor(2, 1);

  lcd.write(byte(0));

  lcd.setCursor(14, 1);

  lcd.write(byte(0));

  lcd.setCursor(15, 1);

  lcd.write(byte(0));

  lcd.setCursor(5, 2);

  lcd.write(byte(0));

  lcd.setCursor(18, 2);

  lcd.write(byte(0));

  lcd.setCursor(1, 2);

  lcd.write(byte(1));

  lcd.setCursor(2, 2);

  lcd.write(byte(1));

  lcd.setCursor(14, 2);

  lcd.write(byte(1));

  lcd.setCursor(15, 2);

  lcd.write(byte(1));

  lcd.setCursor(5, 1);

  lcd.write(byte(1));

  lcd.setCursor(18, 1);

  lcd.write(byte(1));

  delay(1200);

}


void smileyEyes() {  //face animation on lcd screen

  for (int i = 1; i < 6; i++) {

    lcd.setCursor(i, 1);

    lcd.write(byte(0));

  }

  for (int i = 14; i < 19; i++) {

    lcd.setCursor(i, 1);

    lcd.write(byte(0));

  }

  lcd.setCursor(0, 2);

  lcd.write(byte(0));

  lcd.setCursor(6, 2);

  lcd.write(byte(0));

  lcd.setCursor(13, 2);

  lcd.write(byte(0));

  lcd.setCursor(19, 2);

  lcd.write(byte(0));

}


void updateExpression() {  //randomly selects face animation on lcd screen

  int randomInt = random(1, 5);

  lcd.clear();


  if (randomInt == 1) {

    openEyes();

  } else if (randomInt == 2) {

    closeEyes();

  } else {

    smileyEyes();

  }

}


void displayText(String text) {  // breaks the text into lines of 20 characters

  lcd.clear();

  for (int i = 0; i < 4; i++) {

    lcd.setCursor(0, i);

    int startIdx = i * 20;

    if (startIdx < text.length()) {

      lcd.print(text.substring(startIdx, startIdx + 20));

    }

  }

}


void scrollText(String text) {  //formats text for screen

  int totalLength = text.length();

  int displayLength = 80;  // Maximum characters displayed at once (4 lines × 20 chars)

  int scrollStart = 0;


  while (scrollStart < totalLength) {

    String visibleText = text.substring(scrollStart, scrollStart + displayLength);


    // Display the current section of text

    displayText(visibleText);


    // Wait and then scroll

    delay(500);         // Adjust delay for scroll speed

    scrollStart += 20;  // Scroll one line at a time

  }

}