❤️ How to Make a Long Distance Message Box ❤️

by Facio Ergo Sum in Circuits > Electronics

431 Views, 13 Favorites, 0 Comments

❤️ How to Make a Long Distance Message Box ❤️

LadybugMessageBox.png
IMG_2673.JPG
IMG_2680.JPG
LadybugsReaction.gif

It's hard to stay connected in a long-distance relationship, and sometimes texting just isn't enough. If you've ever been in one, you'll know what I mean. So when I found myself stuck in Colorado, while my girlfriend headed back to Virginia for break, I started searching for something to give her that would help us stay in touch. Something more meaningful and tangible.

I had seen those fancy "Loveboxes" online – it's a $200 box that let you send special messages to a loved one. The problem was, my girlfriend boarded her flight in 8 hours. As a self-assured maker and electronics aficionado, I thought "Yeah, I could build that"

Enter the Long-Distance Message Box – a handcrafted device powered by a tinyCore breakout board. The way it works is simple: I send a message through its web interface, and the box chimes to let my girlfriend know that something special has arrived. The best part – she gets to shake it to break open the "letter"!

Luckily, I was able to finish the project in time! My girlfriend loved it so much that she insisted I write an Instructable to share it with others.

Let's build one together!

Supplies

RequiredMaterials.jpg

Electronics:

  1. 1x tinyCore microcontroller (a custom ESP32 board with built-in motion sensing, Wi-Fi, and SD card)
  2. 1x Small OLED or LCD display (I used the ST7735 with 128x160px)
  3. Passive Piezo Buzzer for notifications
  4. 6 Wire Jumpers
  5. 1x LiPo battery with JST PH plug (optional)

Hardware:

  1. Small decorative box with lid (My girlfriend's favorite color is red, and I found the perfect box at Goodwill)
  2. 3D printed façade panels (files included)
  3. Hot glue (and lots of it!)

Tools

  1. Hot glue gun
  2. 3D printer (if you need façade panels)

Software

  1. Arduino IDE
  2. Firebase account (free tier works fine)
  3. (Optional) Basic web hosting service like GitHub Pages (free)

Understanding the Brains of the Operation

What really made this project possible (in the time allotted) was the tinyCore - a custom breakout board I designed specifically for IoT projects like this. Think of it as a PCB Swiss Army knife:

  1. Built-in motion sensing (perfect for our "shake to reveal" feature)
  2. WiFi & BLE already included via ESP32
  3. USB-C charging port with battery management
  4. Integrated SD Card slot for memory
  5. All the serial connections needed for displays, speakers, LEDs, etc.

tinyCore was perfect for this project because it consolidated what would normally be 4-5 separate components into one neat package. This means our message box can be compact, battery-powered, and still have all the functionality of it's commercial counterpart, without costing hundreds of dollars.

If you're interested in learning more about tinyCore, please check out my website.

If you don't have a tinyCore, you could substitute with an ESP32 dev board plus an accelerometer module, though you'll need to modify the wiring and code accordingly. My secondary recommendation is an ESP32-S3 Devkit, and the Adafruit LIS3DH as alternatives.

Finding the Perfect Box

IMG_20241219_143754_829 (1).jpg

Finding just the right box is more important than you might think. It needs to be the right size, have a lid that's easy to access but stays closed when needed, and ideally match your loved one's aesthetic.

I hit the jackpot at Goodwill – a small crimson-red velvet box about the size of a baseball. Originally made to hold a Christmas ornament (which found a happy new home on my roommate's tree, that's still up for some reason?), it perfectly blended with my girlfriend's unique décor style – what I can only describe as "Japanese hobbit hole meets Moroccan boudoir."

The box has a flip-top lid with a small fabric and fake-ivory loop latch, making it perfect for our "shake to open" mechanism. Plus, the rich red color gives it the romantic vibe I was going for.

Preparing the Box and 3D Printed Parts

Screenshot 2025-06-14 092630.png

The Goodwill special was charming but structurally... well, let's just say it had character. The cardboard was a bit flimsy and wouldn't provide enough support for the electronics. I wanted this to feel professionally made!

Luckily, 3D printing saved the day. I designed some rudimentary custom panels to reinforce the box and provide mounting points for the tinyCore and display. The panels also help position everything just right so the display is easily visible when the box is opened.

  1. Measure the internal dimensions of your box carefully
  2. Design (or download) panels that fit snugly inside
  3. Test fit everything before final assembly

Note: I've included my STL files, but since boxes vary widely, you'll likely need to customize them for your specific enclosure.

After I finished the 3D-modeling, I also needed to poke a hole in the bottom on the backside of the box to expose the USB-C for programming and charging.

Electronics Assembly

IMG_20241217_155410_645.jpg
IMG_20241217_155418_768.jpg
IMG_20241217_155403_675.jpg

This is where the tinyCore came in clutch. With most projects, you'd be wrestling with multiple PCBs, a rats' nest of wires, and probably a breadboard. But here, we're keeping it clean and simple, just one female-male DuPont ribbon cable, seven wires, and the buzzer plugged in to the tinyCore's headers.


Here's the full pinout list:


Buzzer:

Positive → A4

Negative → GND


Display:

ESP32 Pin → ST7735 Pin

Pin 10 → CS (Chip Select)

Pin 8 → RST (Reset)

Pin 9 → DC (Data/Command)

Pin 11 → MOSI (SDA/Data In)

Pin 13 → SCLK (Clock)

Pin 12 (Optional) → BL (Backlight)

3.3V → VCC

GND → GND


Since the tinyCore has built-in battery management, I connected a small 1200mAh LiPo battery via the JST plug provided. This gives the box about a week of standby time between charges, but you can go bigger if you have space.

Time for Hot Glue!

IMG_20241217_161229_194.jpg
IMG_20241217_161224_739.jpg
IMG_20241217_161202_573.jpg
IMG_20241219_143747_747.jpg

Now comes the part that always makes me nervous – permanently mounting everything with hot glue. There's no going back if I melted the velvet, and since the box is cardboard.. Good luck getting the electronics back out without destroying it.

Pro tip: Do a "dry run" placement of all components before touching that glue gun! (Definitely not speaking from experience...)

Time to do it for real: First, position the tinyCore inside the box. Then mark where it will sit, and apply hot glue. Last, quickly position the tinyCore and press into place.

This is where I had my near-disaster moment. At first, I glued the tinyCore in backwards!, but luckily I managed to wiggle it into place just before the glue set. Remember, working with a heat-sensitive fabric-covered cardboard box means hot glue is extremely permanent – there are no do-overs! (Just small fudges.)

Once the tinyCore was pressed onto the bottom, I glued the display into the faceplate façade, being careful not to get any on the actual screen. Then I applied a liberal amount of hot glue onto the back of the faceplate and put it in-place, capping off the electronics.

Lastly, the bottom of the box and underside of the lid were covered in a nasty old foil, so I removed those pieces and replaced them with some more 3D-printed covers. I think it looks great!

Tips:

  1. Don't forget to hot-glue the buzzer in place
  2. Route the wires neatly and secure them with small dabs of hot glue
  3. Move fast!

The Code - Arduino IDE

Screenshot 2025-06-14 101726.png
Screenshot 2025-06-14 100357.png

Now to program! The software consists of two parts:

  1. The web interface where you'll send messages
  2. The firmware for the tinyCore

Let's start with the firmware. To tackle this problem, we should break it down to the fundamental behavior: this is a Wi-Fi-connected box that receives text messages and displays them on a screen. When you get a new message, you shake the box and it is revealed!

The best way to model this behavior is with something called a "Finite State Machine" or a Markov Chain. I've included an image of the box's FSM above. Here's how it works:


The message box operates in exactly three states and moves between them based on user actions:

1. SLEEP State

  1. Screen is off to save power
  2. Waiting quietly for user interaction
  3. How to exit: Shake the box → goes to OPEN state

2. UNOPEN State

  1. Shows "You've Got Mail!" notification
  2. Displays mail emoji and "Shake to read" instruction
  3. How to exit: Shake the box → goes to OPEN state

3. OPEN State

  1. Displays the actual message content
  2. Shows timestamp and scrolls long messages
  3. How to exit: Wait 15 seconds with no activity → goes to SLEEP state


Now that we have a simple FSM behavior decided, we can easily turn that into psuedocode. I've also provided a flowchart above that you can visually understand how our program works.

// --------------- Basic Setup ----------------------- //
setupDisplay() // Initialize screen
setupIMU() // Initialize motion sensor
setupWiFi() // Connect to internet
setupFirebase() // Connect to message database

// --------------- Main Program Loop ----------------------- //
REPEAT FOREVER:

// Always check for motion in any state
checkShake()
// Check for new messages (except when unread exists)
IF no unread messages:
checkForNewMessages()
IF new message found:
Play notification tone
Switch to UNOPEN state
// State Machine - Do different things based on current state
SWITCH current state:
CASE SLEEP:
IF first time entering:
fadeDisplay() // Turn off screen
// Just wait for shake (handled by checkShake)
CASE UNOPEN:
IF first time entering:
displayMailNotice() // Show "You've Got Mail!"
Play notification tone
// Wait for shake to read (handled by checkShake)
CASE OPEN:
IF first time entering:
displayMessage() // Show actual message
updateMessageStatus() // Mark as read in database
Start 15-second timer
IF message is long:
scrollMessage() // Show message in chunks
IF 15 seconds pass with no activity:
Switch to SLEEP state

// --------------- Key Functions ----------------------- //
// ----------------------------------------------------- //

// --------------- checkShake() --------------- //
Read motion sensor values (X, Y, Z acceleration)
Calculate total movement = √(X² + Y² + Z²)
IF movement > shake threshold:
SWITCH states:
From SLEEP → OPEN (wake up)
From UNOPEN → OPEN (read message)
Reset activity timer

// --------------- checkForNewMessages() --------------- //
Query Firebase database: "messages/has_new"
IF new message exists:
Get message content and timestamp
Save to local memory
Set hasUnreadMessage = true
RETURN true

// -------------------displayMessage()---------------- //
Clear screen
Process emoji codes in message text
Break text into lines that fit screen width
IF message fits on one screen:
Show entire message in speech bubble
ELSE:
Prepare message for scrolling
Call scrollMessage()
Add timestamp in bottom corner

// ----------------displayMailNotice()---------------- //
Clear screen
Show "You've Got Mail!" in large text
Draw mail emoji icon
Show "Shake to read" instruction

// ----------------updateMessageStatus()-------------- //
Tell database: set "messages/has_new" = false
Tell database: set message "read" = true
Clear local unread flag

Adding Emojis!

Screenshot 2025-06-14 105012.png
Screenshot 2025-06-14 105524.png
Screenshot 2025-06-14 105545.png
Screenshot 2025-06-14 105601.png
Screenshot 2025-06-14 105632.png
Screenshot 2025-06-14 105701.png

It's important to mention the other files besides the main MessageBox Arduino program. The display library did not include emojis, but I wanted to add emoji support because it's important for conveying feeling, so it was easiest to put these into their own header files. Each of these emoji are accessed by using string codes in your messages. The included emojis are currently limited, but I encourage you to make your own. Here's a complete list of the emoji codes:

😊 Face Emojis

  1. 🙂 Smile → :)
  2. 🙁 Sad → :(
  3. 😉 Wink → ;)
  4. 😍 Heart Eyes → :D
  5. 😂 Laughing with Tears → XD
  6. 😇 Angel → 0)
  7. 😎 Cool (Sunglasses) → B)
  8. 😭 Crying → :=(
  9. 😓 Downcast with Sweat → :'(
  10. 😘 Winky Kiss → ;S
  11. 😏 Smirk → ;P
  12. 😨 Surprised→ :O

💝 Icons & Objects

  1. ❤️ Heart → <3
  2. ⭐ Star → *
  3. 💌 Mail/Love Letter → @
  4. 🐞 Ladybug → LB


I had to draw each of these emojis by hand, but (with the help of Claude) I developed a Python tool to help me do it! I've also included that here for your use. This step definitely took the most amount of time, and I had to be careful since my girlfriend's plane was boarding in a few hours. I picked our most commonly used emoji's via text, and I made a special one based on my nickname for her: Ladybug.

The Code: Web Interface and Firebase Setup

Screenshot 2025-06-14 110609.png
Screenshot 2025-06-14 111723.png
Screenshot 2025-06-14 112023.png
Screenshot 2025-06-14 112110.png
Screenshot 2025-06-14 112610.png
Screenshot 2025-06-14 112411.png

For the web interface, I created a simple static HTML page that sends messages to Firebase. Instructables doesn't support HTML file uploads, but you can find the files for this on the project's GitHub repository. I made a couple versions. One plain and one sappy :3

We also need to setup Firebase to be able to store all of the messages and update both ends. To do this, we'll start by making a new Project, create a Real-time Database, and start it in Test Mode. Then, you'll want to add the JSON structure. You can do this by clicking the "+" icon on the database, and adding fields. First, add an empty field called "messages" (Key = "messages", Value = nothing). This will let you add things underneath it. Match the structure exactly as I have in the screenshots above. Don't worry about adding content/messages/dates, that will all be filled in by the box and web interface.

Once that's configured, update the API link in your Message Box Sender UI, and try sending a message! Does it update? Great!

(Also, don't forget to update your Arduino program with this new API endpoint!)

Testing the System

Screenshot 2025-06-14 113345.png
IMG_20241219_143828_128 (1).jpg

Before gifting your creation, thorough testing is essential. I discovered during testing that very long messages would sometimes cut off. To fix this, I implemented a scrolling text feature that automatically shows the full message regardless of length.

I also discovered that the shake detection was a bit too sensitive initially, sometimes triggering when the box was just moved slightly. Adjusting the threshold in the code fixed this issue.

After those fixes, the box works great! I was able to successfully send myself messages via the web interface, and the box would do a cute little chime! I could shake the box to open them, and with scrolling implemented, it was time for the big reveal!

The Moment of Truth - Gifting the Box

LadybugsReaction.gif

This is my favorite part about making handmade gifts - the moment you present your creation to your loved one.

I preloaded a special poem in the box before giving it to my girlfriend. When she opened it, the box immediately started buzzing, prompting her to shake it. As she did, my poem appeared on the screen, word by word, in a typewriter effect:

"My Only Wish

I wish I may, I wish I might, or so the poem goes.

But another thoughtful limerick, I would rather compose..."

Her reaction was priceless - a mix of surprise, joy, and that special look that says, "I can't believe you built this!" She called it her "robot in a box" (and I decided not to correct her).

I gave her a big hug, but the moment was short, because now we had a plane to catch!

Conclusion

image14 (1).jpeg
PXL_20250502_032238562.MP.jpg
PXL_20250502_032240799.jpg

In an age of instant messaging and social media, there's something special about creating a dedicated device just for sharing moments with your significant other. It brings back that feeling of anticipation, like waiting for a letter in the mail, but with a modern twist.

Building the Message Box was really fun, and having a tight deadline was exciting! But most importantly, it created a unique connection between me and my partner during our time apart. Each message became an event, a moment of joy rather than just another notification among dozens on her phone.

So whether you're in a long-distance relationship, want to surprise your partner with something unique, or just enjoy building things that bring people together, I hope this project inspires you to create something special for someone you care about.

Because at the end of the day, it's not just about the cool tech - it's about using our skills to create moments of genuine human connection.

P.S. The box has become such a hit that my girlfriend now keeps it on her nightstand and checks it every morning and evening. Success!