Final Project: Reimagining the LoveBox
Made by Tyler White, Sarah Kuzniewski, Bennett Goeke and Tobi Aina · UNLISTED (SHOWN IN POOLS)
Made by Tyler White, Sarah Kuzniewski, Bennett Goeke and Tobi Aina · UNLISTED (SHOWN IN POOLS)
Use calm technology to improve the everyday digital messaging experience.
Created: December 18th, 2023
Our group found the reading on calm technology very interesting and persuasive. When brainstorming ideas we all talked about our dislike of notifications that basically yell at you to pay attention to them and how the different ways that can negatively affect you – jarring sound if you’re not expecting it, a clear disruption from your focus on something else. So one of our main areas for a potential use case was how can we use calm technology to improve on some current design and/or notification system.
From there we settled on coming up with a less intrusive way to message other people. We all preferred texting over phone calls, but the fact you have to use your phone to receive / look at messages when you get them opens you up to a whole slew of other distractions when you’re checking the message. It also requires you to keep your phone nearby, which puts you at risk of being distracted by notifications for other apps as well.
We considers other options as well, but several of the other ideas took us in directions that we thought would be too complex for our level of understanding and the time available to create the project. We talked about smart blinds, smart glasses, and even how to design a google home / Alexa that would incorporate more light options for notifications instead of just sound notifications.
We selected the LoveBox because it was an example of an IoT device that implemented SMS messaging while also being ambient. It serves as a great alternative to the other connected devices that users need to be consistently engaged with to be effect, like normal sms. The LoveBox provides an example of calm technology influencing communication apps, but still allowing for meaningful and timely communication in the future of ubiquitous computing. Users still have all the connectedness of a regular phone, but carry much less propensity for distraction and/or disruption. We believe this increases the emotional or sentimental value that is attached to messages by refocusing all the emphasis on the actual message instead of it being split across several different potential distraction points.
The approach involved studying the Lovebox's design, functionality, and user experience. We aimed to replicate its core features while incorporating our own twist. We didn’t divert too much from the original design of the box. Only making alterations that would get us to a proof of concept sooner. We removed the lid and added a button, as we thought this would make the development of the box much simpler. We’d seen buttons on other similar devices that we’d researched, and they seemed to be a positive addition to the overall concept that created a simple but meaningful way for users to engage with the device and notify the sender they’d received the message.
We tried to revive the device's essential features. We mainly focused on the integration of the LCD screen as well as the unique feature of the spinning heart, which acts as the notification signal that a message has been received. We thought it created a delightful and engaging experience for the user and was one of the most important aspects of the device.
The journey began with a detailed analysis of Lovebox mechanics. We then sketched out our prototype, iterated on design, and implemented the chosen features. We went through a few different ideas on how to incorporate the main parts, if we’d be able to figure out the screen, what the size of the box would be, and how we’d create the box. But overall our initial ideas were what we followed through on.
Experimentation involved moments of success, such as achieving the spinning heart effect, as well as challenges, such as fine-tuning the mechanism for optimal performance. It was tough at first to get the screen to work and we needed some support with figuring out the right code for it. We also were unsure of which motor would be the best to use. We chose to use a DC motor but eventually were told other options would likely have been better because they’d have provided a more options when programming.
Initial plans underwent adjustments, particularly in the choice of materials for the spinning heart mechanism, based on real-world testing. At first we weren’t sure how to design the heart or how to attach it, but after a few trials we figured out what we thought would work best for the prototype.
Regarding code, the first thing worked on was the screen. Getting images to display accurately was the first real test. Then the work shifted to adding the motor and button that triggered the images onto the display. Regarding the design, the first thing worked on was measuring out the box the device would be housed in and then deciding what material would be used and how to cut it. From there the box was cut, stained and pieced together.
The biggest challenge was the wiring of all the components because they needed to be packaged in a tight box. This meant that wires would often come out slightly and it would be very hard to diagnose where the problem wires were. On the design side of the physical space we didn’t run into many problems. We were unsure however about how the motor would stay in place to spin properly but we were able to resolve that when designing the box itself.
If we were doing this again we would try to work on a better way of packaging the device and find a better way to cleanly connect components to the breadboard with less jump cables. How we went about it worked, but it was very congested. We’d also work to create a better experience regarding interacting with the device to inform it the message has been received. The button we were using turned out to be cumbersome and placed in an inconvenient location.
// This #include statement was automatically added by the Particle IDE.
#include <Adafruit_GFX_RK.h>
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_ST77xx.h"
// This #include statement was automatically added by the Particle IDE.
#include "Adafruit_ST7789.h"
// This #include statement was automatically added by the Particle IDE.
// #include "Adafruit_ST7735.h"
//#include "Adafruit_ST7789.h"
#include "SPI.h"
//#include "Adafruit_GFX.h"
// You can't really have two of these on the same pins, but this is just to make sure it compiles
// It's not a working example.
// I've removed the examples because it's not possible to have dependencies for individual examples
// and the necessary libraries won't be included and the build will fail. Since other boards like
// the miniTFTWing have their own examples, you should just test this library with those examples.
Adafruit_ST7789 st7789(&SPI1, D2, D3, D4);
int buttonPin = D3;
int solPin = D2;
bool displaySmile = false;
bool displaySad = false;
bool displayHeart = false;
bool displayCry = false;
bool displayUnsat = false;
#if defined(ARDUINO_FEATHER_ESP32) // Feather Huzzah32
#define TFT_CS 14
#define TFT_RST 15
#define TFT_DC 32
#elif defined(ESP8266)
#define TFT_CS 4
#define TFT_RST 16
#define TFT_DC 5
#else
// For the breakout board, you can use any 2 or 3 pins.
// These pins will also work for the 1.8" TFT shield.
#define TFT_CS A5
#define TFT_RST -1 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC D8
#endif
// OPTION 1 (recommended) is to use the HARDWARE SPI pins, which are unique
// to each board and not reassignable. For Arduino Uno: MOSI = pin 11 and
// SCLK = pin 13. This is the fastest mode of operation and is required if
// using the breakout board's microSD card.
//Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// OPTION 2 lets you interface the display using ANY TWO or THREE PINS,
// tradeoff being that performance is not as fast as hardware SPI above.
#define TFT_MOSI 11 // Data out
#define TFT_SCLK 13 // Clock out
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
float p = 3.1415926;
void setup(void) {
Serial.begin(9600);
tft.fillScreen(ST77XX_BLACK);
// Use this initializer (uncomment) if using a 1.3" or 1.54" 240x240 TFT:
//tft.init(240, 240); // Init ST7789 240x240
// OR use this initializer (uncomment) if using a 1.69" 280x240 TFT:
//tft.init(240, 280); // Init ST7789 280x240
// OR use this initializer (uncomment) if using a 2.0" 320x240 TFT:
//tft.init(240, 320); // Init ST7789 320x240
// OR use this initializer (uncomment) if using a 1.14" 240x135 TFT:
//tft.init(135, 240); // Init ST7789 240x135
// OR use this initializer (uncomment) if using a 1.47" 172x320 TFT:
tft.init(200, 240); // Init ST7789 172x320
// tft.init(320, 320); //clears screen
// OR use this initializer (uncomment) if using a 1.9" 170x320 TFT:
//tft.init(170, 320); // Init ST7789 170x320
// SPI speed defaults to SPI_DEFAULT_FREQ defined in the library, you can override it here
// Note that speed allowable depends on chip and quality of wiring, if you go too fast, you
// may end up with a black screen some times, or all the time.
//tft.setSPISpeed(40000000);
// a single pixel
// tft.drawPixel(tft.width()/2, tft.height()/2, ST77XX_GREEN);
// delay(500);
pinMode(buttonPin, INPUT_PULLUP);
Particle.function( "emojiSmile", activateSolenoid );
Particle.function( "emojiSad", activateSad );
Particle.function( "emojiHeart", activateHeart );
Particle.function( "emojiCry", activateCry );
Particle.function( "emojiUnsat", activateUnsat );
pinMode(solPin, OUTPUT);
}
void loop() {
if( displaySmile ){
doSmile( );
displaySmile = false;
}
if( displaySad ){
doSad( );
displaySad = false;
}
if( displayHeart ){
doHeart( );
displayHeart = false;
}
if( displayCry ){
doCry( );
displayCry = false;
}
if( displayUnsat ){
doUnsat( );
displayUnsat = false;
}
}
void doSmile() {
// digitalWrite(solPin, HIGH);
tft.fillScreen(ST77XX_BLACK);
for (int i = 0; i < 2; i++) {
digitalWrite(solPin, HIGH);
delay(100);
digitalWrite(solPin, LOW);
delay(1000);
}
// Wait for the button to be pressed
while (digitalRead(buttonPin) != LOW) {
// You can add optional delay here to avoid busy-waiting
// delay(100);
}
// Button is pressed, proceed to displaySmiley
displaySmiley();
}
void doSad(){
tft.fillScreen(ST77XX_BLACK);
for (int i = 0; i < 2; i++) {
digitalWrite(solPin, HIGH);
delay(100);
digitalWrite(solPin, LOW);
delay(1000);
}
// Wait for the button to be pressed
while (digitalRead(buttonPin) != LOW) {
// You can add optional delay here to avoid busy-waiting
// delay(100);
}
// Button is pressed, proceed to displaySmiley
displaySady();
}
void doHeart(){
tft.fillScreen(ST77XX_BLACK);
for (int i = 0; i < 2; i++) {
digitalWrite(solPin, HIGH);
delay(100);
digitalWrite(solPin, LOW);
delay(1000);
}
// Wait for the button to be pressed
while (digitalRead(buttonPin) != LOW) {
// You can add optional delay here to avoid busy-waiting
// delay(100);
}
// Button is pressed, proceed to displaySmiley
displayHearty();
}
void doCry(){
tft.fillScreen(ST77XX_BLACK);
for (int i = 0; i < 2; i++) {
digitalWrite(solPin, HIGH);
delay(100);
digitalWrite(solPin, LOW);
delay(1000);
}
// Wait for the button to be pressed
while (digitalRead(buttonPin) != LOW) {
// You can add optional delay here to avoid busy-waiting
// delay(100);
}
// Button is pressed, proceed to displaySmiley
displayCryFace();
}
void doUnsat(){
tft.fillScreen(ST77XX_BLACK);
for (int i = 0; i < 2; i++) {
digitalWrite(solPin, HIGH);
delay(100);
digitalWrite(solPin, LOW);
delay(1000);
}
// Wait for the button to be pressed
while (digitalRead(buttonPin) != LOW) {
// You can add optional delay here to avoid busy-waiting
// delay(100);
}
// Button is pressed, proceed to displaySmiley
displayUnsatFace();
}
int activateCry( String command ){
displayCry = true;
return 1;
}
int activateUnsat( String command ){
displayUnsat = true;
return 1;
}
int activateHeart( String command ){
displayHeart = true;
return 1;
}
int activateSolenoid( String command ){
displaySmile = true;
return 1;
}
int activateSad( String command ){
displaySad = true;
return 1;
}
void displayCryFace() {
// draw face
tft.fillScreen(ST77XX_BLACK);
tft.fillCircle(120, 120, 50, ST77XX_YELLOW);
//draw mouth
tft.fillCircle(145, 120, 18, ST77XX_BLACK);
// draw left eye
tft.fillCircle(110, 145, 15, ST77XX_BLACK); // Swap X and Y coordinates and adjust
tft.fillCircle(110, 145, 5, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
tft.fillRect(110, 120, 22.5, 41, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
// draw right eye
tft.fillCircle(110, 105, 15, ST77XX_BLACK); // Swap X and Y coordinates and adjust
tft.fillCircle(110, 105, 5, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
tft.fillRect(110, 80, 22.5, 41, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
tft.fillRect(105, 145, 120, 10, ST77XX_BLUE); // Swap X and Y coordinates
tft.fillRect(105, 95, 120, 10, ST77XX_BLUE); // Swap X and Y coordinates
}
void displayUnsatFace() {
// draw face
tft.fillScreen(ST77XX_BLACK);
tft.fillCircle(120, 120, 50, ST77XX_YELLOW);
// draw mouth (unsatisfied)
tft.fillRect(140, 100, 12.5, 45, ST77XX_BLACK); // Adjust Y coordinate and size for unsatisfied mouth
for (int i = 0; i < 5; ++i) {
// draw eyes (horizontal rectangles for unsatisfied eyes)
tft.fillRect(100, 95, 20, 10, ST77XX_BLACK); // Adjust X, Y coordinates, and size for left eye
tft.fillRect(100, 135, 20, 10, ST77XX_BLACK); // Adjust X, Y coordinates, and size for right eye
delay(300);
tft.fillRect(100, 95, 20, 10, ST77XX_YELLOW); // Adjust X, Y coordinates, and size for left eye
tft.fillRect(100, 135, 20, 10, ST77XX_YELLOW); // Adjust X, Y coordinates, and size for right eye
delay(100);
tft.fillRect(110, 90, 10, 20, ST77XX_BLACK); // Adjust X, Y coordinates, and size for right eye
tft.fillRect(110, 140, 10, 20, ST77XX_BLACK); // Adjust X, Y coordinates, and size for left eye
delay(1000);
tft.fillRect(110, 90, 10, 20, ST77XX_YELLOW); // Adjust X, Y coordinates, and size for right eye
tft.fillRect(110, 140, 10, 20, ST77XX_YELLOW); // Adjust X, Y coordinates, and size for left eye
delay(150);
}
tft.fillRect(110, 90, 10, 20, ST77XX_BLACK); // Adjust X, Y coordinates, and size for right eye
tft.fillRect(110, 140, 10, 20, ST77XX_BLACK); // Adjust X, Y coordinates, and size for left eye
}
void displaySmiley() {
// draw face
tft.fillScreen(ST77XX_BLACK);
tft.fillCircle(120, 120, 50, ST77XX_YELLOW);
// draw mouth
tft.fillCircle(130, 120, 30, ST77XX_BLACK); // Swap X and Y coordinates and adjust
tft.fillRect(85, 90, 45, 65, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
// loop for blinking eyes
for (int i = 0; i < 5; ++i) {
// draw eyes
tft.fillCircle(110, 100, 10, ST77XX_BLACK); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_BLACK); // Swap X and Y coordinates and adjust
delay(500); // adjust the delay time to control the blinking speed
// clear eyes
tft.fillCircle(110, 100, 10, ST77XX_YELLOW); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
delay(500); // adjust the delay time to control the blinking speed
}
tft.fillCircle(110, 100, 10, ST77XX_BLACK); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_BLACK); // Swap X and Y coordinates and adjust
}
void displaySady() {
// draw face
tft.fillScreen(ST77XX_BLACK);
tft.fillCircle(120, 120, 50, ST77XX_YELLOW);
// draw mouth
tft.fillCircle(145, 120, 20, ST77XX_BLACK); // Swap X and Y coordinates and adjust
tft.fillCircle(145, 120, 10, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
tft.fillRect(145, 100, 22.5, 41, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
// loop for blinking eyes
for (int i = 0; i < 5; ++i) {
// draw eyes
tft.fillCircle(110, 100, 10, ST77XX_BLACK); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_BLACK); // Swap X and Y coordinates and adjust
delay(500); // adjust the delay time to control the blinking speed
// clear eyes
tft.fillCircle(110, 100, 10, ST77XX_YELLOW); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_YELLOW); // Swap X and Y coordinates and adjust
delay(500); // adjust the delay time to control the blinking speed
}
tft.fillCircle(110, 100, 10, ST77XX_BLACK); // Swap X and Y coordinates
tft.fillCircle(110, 140, 10, ST77XX_BLACK); // Swap X and Y coordinates and adjust
}
void displayHearty() {
// draw heart
tft.fillScreen(ST77XX_BLACK);
// draw triangle for the top part of the heart
tft.fillTriangle(170, 120, 110, 70, 110, 170, ST77XX_RED);
// draw left half of heart using a circle
tft.fillCircle(100, 100, 30, ST77XX_RED);
// draw right half of heart using a circle
tft.fillCircle(100, 140, 30, ST77XX_RED);
for (int i = 0; i < 3; ++i) {
tft.fillTriangle(170, 120, 110, 70, 110, 170, ST77XX_BLACK);
// draw left half of heart using a circle
tft.fillCircle(100, 100, 30, ST77XX_BLACK);
// draw right half of heart using a circle
tft.fillCircle(100, 140, 30, ST77XX_BLACK);
delay(50); // adjust the delay time to control the blinking speed
tft.fillTriangle(170, 120, 110, 70, 110, 170, ST77XX_RED);
// draw left half of heart using a circle
tft.fillCircle(100, 100, 30, ST77XX_RED);
// draw right half of heart using a circle
tft.fillCircle(100, 140, 30, ST77XX_RED);
delay(50); // adjust the delay time to control the blinking speed
}
// draw left half of heart using a circle
tft.fillCircle(100, 100, 30, ST77XX_RED);
// draw triangle for the bottom part of the heart
tft.fillTriangle(170, 120, 110, 70, 110, 170, ST77XX_RED);
// draw right half of heart using a circle
tft.fillCircle(100, 140, 30, ST77XX_RED);
}
Click to Expand
Our team acquired knowledge in mechanical design, microcontroller programming, user experience considerations in IoT, and what goes into designing an meaningful and effective physical space for a product. The professors who engaged with us on pitch day also provided lots of tips on how to improve on our design that most of us had not thought of before.
Future steps may involve enhancing connectivity, exploring mobile app integration, and refining the design for a market-ready product. Other things would be to consider different ways of interacting with the box other than through the button. We got some good comments about touched based interactions as well as knocking interactions. This would make interacting with the box more intimate than using a button.
This project is only listed in this pool. Be considerate and think twice before sharing.
A hands-on introductory course exploring the Internet of Things and connected product experiences.
Use calm technology to improve the everyday digital messaging experience.