Allie's Affirmation Dog
By Team Walnuts: Andreas, Tippi, Myles
Final Documentation
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.
An overall view of the device in its idle state.
An overall view of the object resting on a table in IDeATe for scale.
A close up view of the eyes in the idle state.
A close up view of the eyes when the continue button is pressed.
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
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
}
}