Macintosh-Style Desk Clock With a Paper Shell

by ayushmaan45 in Circuits > Apple

2111 Views, 26 Favorites, 0 Comments

Macintosh-Style Desk Clock With a Paper Shell

Picsart_25-12-03_20-30-55-836.jpg
Picsart_25-12-03_20-34-10-878.png
Picsart_25-12-03_20-31-54-290.png

Hi, I'm Ayushmaan, an Electrical and Electronics Engineering student. I am always fascinated by retro tech, and I especially love those old Macintosh Classics. For the Clocks Contest I wanted to make something that feels vintage but works with modern hardware. Not just a clock on a screen — something that looks like it came from the 80s but actually pulls data from the internet.

Originally, I was planning to 3D-print the whole body, but honestly, everyone does that nowadays. I wanted to build something that anyone could make, even without a 3D printer. Paper is cheap and available everywhere; with a bit of patience, it can look pretty clean and sturdy. So, I made everything with paper only. It ended up better than expected.

The enclosure was designed in Blender, unfolded as printable templates using Pepakura, and built by hand. Inside, I'm showing real-time information from the internet through the ESP32 with a TFT display. The display displays the current time, temperature, humidity, weather description, and AQI. I also designed the UI myself to fit the old-style Macintosh.

What you can learn from this project:

The build allowed me to experiment with a lot while building this. A person building this will learn:

  1. How to make paper templates from a Blender model using Pepakura
  2. How to create strong and accurate structures using only paper.
  3. How to wire and set up a TFT display with ESP32
  4. How to design a UI layout in Lopaka
  5. How to use a Lopaka UI inside ESP32 code
  6. How to set up and use the OpenWeather API to get temperature, humidity, weather and AQI

The whole point of this build is to mix electronics with DIY creativity. You don't need expensive tools, just some basic components, paper, time, and interest, and in the end you get a desk clock that looks retro but works like a smart IoT device.

Supplies

20251201_202321.jpg

Electronics

  1. ESP32 board
  2. TFT display (SPI 320×240 in my case)
  3. Lithium battery
  4. Charging module
  5. Push button
  6. Jumper wires / normal wires
  7. USB cable (for uploading code and powering)

Paper & Craft Materials

  1. printed templates
  2. 220 GSM ivory sheet paper
  3. Scissors / cutter
  4. Glue
  5. Double-sided foam tape
  6. Scale (for scoring and folding)

Software

  1. Blender (for working with the 3D model)
  2. Pepakura Designer (for unfolding and printing templates)
  3. Arduino IDE (for coding the ESP32)
  4. Lopaka (for designing the UI)
  5. OpenWeatherMap account (for API key)

Designing and Making the Paper Template

WhatsApp Image 2025-12-02 at 10.51.04.jpeg

Before building anything physically, I first worked on the digital model so I could create printable templates for the paper body. This whole part can be divided into three smaller sub-steps:

  1. Finding and importing the Macintosh model I downloaded a Macintosh Classic model and brought it into Blender)
  2. Editing and simplifying the model for papercraft (adjusting the mesh so it’s easier to unfold and build using paper)
  3. Unfolding the final model to make the printable templates (using Pepakura to generate the actual cut-and-glue sheets)

All of this was done on the computer before touching any paper.

The goal here was to get a Macintosh-style body that looks accurate but is still practical enough to build using only paper and glue.

In the next steps, I’ll explain each of these three parts separately.

Finding and Importing the Macintosh Model

InShot_20251202_113008136.gif
WhatsApp Image 2025-12-02 at 10.51.02.jpeg
WhatsApp Image 2025-12-02 at 10.51.05.jpeg

Instead of modelling the whole Macintosh from scratch, I used an existing Macintosh Classic model by Kreems on Sketchfab. It already had the right look and proportions, so it made sense to start from there.

I downloaded the model in .OBJ format and imported it into Blender.

When you import an OBJ from Sketchfab, the texture usually doesn’t show automatically, so I had to link it manually. I did it like this:


File > External Data > Find Missing Files
(select the folder that contains the texture)

After doing this, the model displayed correctly with its original texture.

At this stage I wasn’t worried about perfection — I just wanted the model inside Blender, visible with the texture, so I could begin editing it later.

That’s all for this step. The main goal here was just to get the Macintosh model into Blender and visible properly.

Editing and Simplifying the Model for Papercraft

InShot_20251202_112815349.gif
InShot_20251202_112709492.gif
InShot_20251202_112612522.gif
InShot_20251202_112929361.gif

The Sketchfab model looked great visually, but it wasn’t ready to be turned into a paper build directly. For papercraft, the geometry needs to be clean, simple and unfoldable — otherwise Pepakura will create too many tiny pieces or weird folds.

So in Blender, I edited the model to make it paper-friendly and scaled it according to my TFT display.

Here’s everything I changed:

  1. Resized the model according to my screen The most important thing was the front screen opening. I measured the visible area of my TFT and scaled the whole Macintosh model so the cutout matches it properly. If the cutout is even a few millimeters off, the display won’t align later.
  2. Removed bevel edges The original model had soft rounded edges. They look good in 3D but become a nightmare in paper form because Pepakura splits them into many tiny strips. Flattening them makes the build stronger and easier.
  3. Added loop cuts to fix n-gons Some faces had more than 4 sides. Pepakura unfolds quads nicely but struggles with n-gons. A few loop cuts fixed that.
  4. Kept only the essential details I left the floppy slot, vents and overall shape because they make it instantly recognizable as a Macintosh. But I removed unnecessary tiny features that don’t help in a paper version.

Once the model was cleaned up and scaled correctly for the screen, I exported it again as .OBJ. This is the version used in Pepakura to make the print templates.

Unfolding the Model in Pepakura and Exporting the Templates

InShot_20251202_112451661.gif
InShot_20251202_113222664.gif

Once the cleaned and resized .OBJ file was ready, I opened it in Pepakura Designer to turn the 3D Macintosh into flat printable parts.

When the model loads, Pepakura shows it in 3D on the left and the unfold (the flat parts) on the right. At first, the unfold won’t look clean, pieces will be scattered everywhere, so this step is mostly about arranging everything properly.

Here’s what I did:

  1. Imported the texture along with the model
  2. Pepakura doesn’t always attach textures automatically, so I used
  3. Setting > Texture Settings > Specify Texture Image
  4. This makes the printed parts match the Macintosh texture.
  5. Unfolded the model using “Unfold” button
  6. It creates the basic layout, but you still have to fix a few things manually.
  7. Adjusted the layout to fit A4 pages
  8. I moved, rotated and grouped pieces so everything prints cleanly on standard paper without wasting space.
  9. Split some large faces and joined some tiny ones
  10. Pepakura sometimes cuts a single surface into too many pieces, or keeps something too large. I edited those until the templates looked manageable.
  11. Generated glue flaps where needed
  12. The auto flaps feature mostly works fine, but I made sure tabs aren’t placed in weird angles or on visible areas.

After the layout was done, I exported the templates as PDF files. These PDF sheets are what I printed on thick paper in the next step.

Downloads

Printing and Preparing the Paper Parts

20251121_194125.jpg
InShot_20251203_190639278.gif

I printed the Pepakura templates on 220 GSM ivory sheet paper. This thickness turned out to be perfect — strong enough to hold shape, but still easy to fold and cut. Anything much thinner becomes floppy, and anything much thicker becomes hard to fold cleanly.

Once printed, I didn’t rush straight into gluing. A bit of prep makes the whole assembly smoother later. Here’s what I did:

  1. Cut all the pieces cleanly I used a cutter for long straight lines and scissors for curves. Sharp cutting really affects the final look of the build.
  2. Scored all the dotted fold lines I used the back of a cutter / blunt blade to score before folding. This gives those sharp, crisp folds instead of wrinkled ones.
  3. Pre-folded every part before glue This helps understand how the shape closes and where each tab attaches. After folding everything once, I already had a mental picture of how it would all come together.
  4. Sorted parts into groups I kept all the “front” related pieces together, “side” pieces together, “top/back” together etc This avoids chaos when you start gluing.


Downloads

Assembling the Macintosh Body

20251122_103857.jpg
20251122_104035.jpg
20251122_104108.jpg

The papercraft part wasn’t very complicated in my case it mainly consisted of the main body and the front panel.

Here’s how I assembled it:

  1. Main body shell I glued the big pieces first so the main box shape was formed. Once this part is done, the whole model becomes stable and much easier to handle.
  2. Front panel (separate piece) I built the front panel separately — the square frame with the display cutout. Since the screen goes right behind this opening later, I didn’t attach the front panel to the main body yet. Keeping it separate makes installing the TFT much easier afterward.
  3. Details (printed textures) In this model, the vents and floppy slot were already printed on the paper itself, so there was no extra construction here — just clean folding and gluing so everything aligns nicely.

A small tip that helped me:

I kept Pepakura Designer open while assembling. Even though this model wasn’t very complex, checking the 3D preview during gluing avoids mixing up sides or orientation.

BREADBOARD SETUP

20251201_200347.jpg

Before putting anything inside the paper body, I tested the electronics separately on a breadboard. This step is important because once the display goes into the paper shell, it becomes harder to plug/unplug wires or fix mistakes.

So I connected the ESP32 and the TFT display on a breadboard first.

The wiring I used (yours may vary slightly depending on display model):

3.3V → VCC

GND → GND

D23 → MOSI

D19 → MISO

D18 → SCK

D15 → CS

D2 → DC

D4 → RESET

D21 → LED / BL

Once the wiring was done, I powered it through USB just to make sure the screen turned on and there were no loose connections.

At this stage, I wasn’t worried about live weather data yet. The goal was simply to:

  1. confirm the display is working,
  2. confirm the pins are correct,
  3. and make sure nothing flickers or resets when the ESP32 refreshes the screen.

This makes the later steps much safer and saves the paper model from possible rework.

Coding (what We’ll Build and Why)

We’re building a tiny weather-desktop UI on an ESP32 + 320×240 TFT. The final system shows time, temperature, humidity and AQI in a clean “Mac-like” UI. The job is split into three parts so it’s easy to follow:

  1. Design the UI — create the visuals in Lopaka (the tool you used) and export bitmaps/arrays for the TFT.
  2. Fetch live data — get real weather, temp and humidity from OpenWeather (or similar) and parse the JSON on the ESP32.
  3. Glue them together — draw the UI with Adafruit_GFX / Adafruit_ILI9341 and update only the UI parts that change (time, numbers) so the screen doesn’t flicker.

Why this split? Because each part is different skill: art (Lopaka), networking (HTTP + JSON) and embedded graphics. Splitting makes debugging easier — test the UI first, then the API, then integrate.

Quick list of libs & hardware used

  1. Board: ESP32 (any common dev board)
  2. Display: ILI9341 320×240 TFT (Adafruit or compatible)
  3. Libraries (Arduino IDE):
  4. Adafruit_GFX
  5. Adafruit_ILI9341
  6. WiFi.h
  7. HTTPClient.h
  8. ArduinoJson.h
  9. NTPClient.h + WiFiUdp.h (for clock)
  10. OpenWeather (or OneCall) API key and coordinates for your location.

Coding Part 1: Designing the Macintosh-Style UI (Using Lopaka)

Screenshot 2025-12-02 185113.png

This is the art side: draw your UI in Lopaka, export it, and prepare it for the ESP32.

What we do in Lopaka

  1. Design at 320×240 (the exact TFT resolution).
  2. Use simple large text blocks for time and values, and small bitmaps for icons (cloud, temp, warning).
  3. Keep key text positions fixed: e.g. time at (15,85), top rectangle at y≈14, bottom rectangle at y≈184 — this makes code easy.

Exporting and converting

  1. Export Lopaka layers as bitmap arrays (usually 1-bit or 16-bit). Lopaka gives something like:

static const uint16_t PROGMEM image_Layer_2_pixels[] = { 0xD661, 0x0000, ... };
void drawImage() {
tft.pushImage(x, y, w, h, image_Layer_2_pixels);
}
  1. If Lopaka exports 1-bit masks (byte arrays), use tft.drawBitmap(x,y,bits,w,h,color) for Adafruit. If Lopaka exported RGB565 arrays, use pushImage() from TFT_eSPI or convert to drawRGBBitmap() style arrays expected by Adafruit.
  2. Put all exported arrays at the top of your sketch (or in a separate header file like lopaka_images.h) so they’re easy to include and reuse.

Practical tips

  1. Keep icon sizes small (16×16, 22×32, 32×32) to save RAM/flash.
  2. If using many icons, move them to PROGMEM: static const unsigned char PROGMEM ....
  3. Test one image at a time: draw a single bitmap at known coordinates first.

Example: (how to keep a Lopaka image in code)


// lopaka export (example)
static const unsigned char PROGMEM image_cloud_bits[] = { /* ... */ };
...
tft.drawBitmap(39, 26, image_cloud_bits, 34, 32, ILI9341_BLACK);


Coding Part 2: Weather & Data (OpenWeather, Keys, Quotas)

OpenWeather v2.5 is the free tier that gives real temperature and humidity without needing a credit card.

API URL Format


https://api.openweathermap.org/data/2.5/weather?lat=YOUR_LAT&lon=YOUR_LON&appid=YOUR_KEY&units=metric

Also you will have to add your Latitude and Longitude, For my location (Haridwar) it was like:


&lat=29.9230&lon=78.1091

What We Get From the API

The JSON response contains:


"main": {
"temp": 13.21,
"humidity": 74
},
"weather": [
{ "main": "Clouds" }
]

From here we extract:

  1. temp
  2. humidity
  3. weather[0].main

When Do We Call the API?

Calling too often wastes your free quota, so we update only every 5 minutes:


if (millis() - lastWeather > 300000) {
lastWeather = millis();
fetchWeather();
}

Parsing the JSON

We use ArduinoJson:


DynamicJsonDocument doc(2048);
deserializeJson(doc, payload);

String condition = doc["weather"][0]["main"];
int humidity = doc["main"]["humidity"];
int temperature = int(doc["main"]["temp"]);

Updating the Screen Without Breaking the UI

This part is important:


tft.setTextColor(ILI9341_BLACK, oldPaper);
tft.setCursor(x, y);
tft.print(NewValue);

Using background color removes the old text automatically.

  1. This prevents overlapping numbers or “ghost text”.

Coding Part 3:the Final Code (how to Merge Lopaka UI + Live Data)

This is the step where everything finally clicks:

your UI from Lopaka, live weather from OpenWeather, and real-time clock updates— all running together on the ESP32 and TFT.

The ESP32 handles three jobs at once:

  1. Draw the UI (once) → backgrounds, rectangles, icons
  2. Update the time (every second)
  3. Fetch new weather (every few minutes)

To keep the screen smooth, we do partial updates only.

We never redraw the entire screen again — just the text that changes.

This avoids flicker, ghosting, and that “refresh wipe” effect.

Below is the full coding breakdown, written in a friendly “maker buddy” tone, but still technically solid.

A) Connecting ESP32 to WiFi (Internet Setup)

Before the ESP32 can display live data, it needs WiFi.

This is the first thing we set up in code:


const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_PASSWORD";

In setup():


WiFi.begin(ssid, password);
WiFi.setSleep(false); // prevents random disconnects

while (WiFi.status() != WL_CONNECTED) {
delay(300);
}

Tip:

Tell readers to add this line (optional but helpful during testing):


Serial.println(WiFi.localIP());

If no IP shows, the WiFi section must be fixed before moving on.

B) Real Time Using NTP (Accurate Clock)

ESP32 does not keep accurate time on its own.

So we sync time from the Internet using NTP:


#include <WiFiUdp.h>
#include <NTPClient.h>

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800, 60000);
  1. 19800 = offset for IST (UTC+5:30)
  2. 60000 = refresh NTP once per minute

Updating the clock every second:


timeClient.update();
String currentTime = timeClient.getFormattedTime().substring(0, 5);

Displaying the time cleanly:


tft.setTextColor(ILI9341_BLACK, oldPaper);
tft.setTextSize(10);
tft.setCursor(15, 85);
tft.print(currentTime);

Using the background color (oldPaper) as the second parameter means the old digits get erased automatically.

No flicker, no leftover pixels — clean updates every second.

C) Fetching Weather from OpenWeather (Free 2.5 API)

For this project, the free OpenWeather 2.5 endpoint is enough — it provides:

  1. ✔ Temperature
  2. ✔ Humidity
  3. ✔ Weather condition (“Clear”, “Clouds”, etc.)

Note: AQI requires OneCall 3.0, which needs payment — so this project only shows weather, temp, humidity.

Sample weather URL:


https://api.openweathermap.org/data/2.5/weather?lat=29.9230&lon=78.1091&appid=YOUR_KEY&units=metric

ESP32 fetches this using HTTPClient:


HTTPClient http;
http.begin(url);
int code = http.GET();

if (code == 200) {
String payload = http.getString();

DynamicJsonDocument doc(2048);
deserializeJson(doc, payload);

int temperature = int(doc["main"]["temp"]);
int humidity = doc["main"]["humidity"];
String condition = doc["weather"][0]["main"].as<String>();

updateWeatherUI(temperature, humidity, condition);
}
http.end();

Avoiding API rate limits

OpenWeather’s free tier has daily limits.

So we must not call it every second.

Instead, we fetch new weather every 5 minutes:


static unsigned long lastWeather = 0;

if (millis() - lastWeather > 300000) {
lastWeather = millis();
fetchWeather();
}

This rate is safe and realistic.

D) Drawing Your Lopaka UI (Static Design)

All the visual structure — boxes, labels, icons, etc. — is drawn once using a dedicated function:


void drawUI() {
tft.fillScreen(oldPaper);

// top bar
tft.drawRect(15, 14, 292, 42, ILI9341_BLACK);

// bottom bar
tft.drawRect(14, 184, 292, 42, ILI9341_BLACK);

// labels
tft.setCursor(58, 199); tft.println("TEMP:");
tft.setCursor(211, 199); tft.println("AQI:");

// icons from Lopaka
tft.drawBitmap(20, 19, image_weather_cloud_sunny_bits, 34, 32, ILI9341_BLACK);
tft.drawBitmap(23, 189, image_weather_temperature_bits, 32, 32, ILI9341_BLACK);
}

This function is called just once inside setup().

This is the cleanest and most stable way to work with TFTs.

( Also i have attached the file of the final code)

Making the Final Circuit

InShot_20251203_191413222.gif
20251202_100226.jpg
20251202_095947.jpg
InShot_20251203_191140402.gif
InShot_20251203_191314203.gif
20251201_225609.jpg

After the breadboard testing was done and everything worked properly, I made the final compact circuit that will go inside the paper body. The final circuit has:

  1. ESP32
  2. TFT display
  3. One push button
  4. Battery
  5. Charging module

I used the same wiring as the breadboard version (no pin changes).

This way, there was no risk of breaking something that already worked.

The connections were done like this:

  1. TFT wired to the ESP32
  2. Push button wired to a free GPIO pin and GND
  3. Battery connected to the charging module
  4. Charging module output powering the ESP32

Once everything was wired, I powered it up to double-check that:

  1. the display showed the UI
  2. the button worked
  3. live weather data still updated normally

At this point, the circuit was fully working and ready to install inside the paper Macintosh.

Installing the Circuit Inside the Paper Macintosh

InShot_20251203_192004253.gif
InShot_20251203_192138110.gif
InShot_20251203_192307356.gif
InShot_20251203_191527759.gif
InShot_20251203_191637475.gif

For the final installation, I just placed everything inside the paper body in a simple and clean way:

  1. I pasted the TFT display on the front panel using double-sided foam tape so the screen lined up perfectly with the window.
  2. I fixed the ESP32 PCB inside the body using hot glue.
  3. I used a small piece of cardboard under the PCB to lift it up to the correct height so it didn’t touch or press the paper.
  4. I fixed the push button on the front panel using hot glue.
  5. After that, I closed the body and the build was complete.

That’s it — nothing complicated. Once everything was fixed in place, the clock looked like a tiny Macintosh from outside but worked like a smart IoT device.

Conclusion

InShot_20251203_192353899.gif
FBV2516MIPAM9TF.jpg
FJF0VTLMIPAM9S8.png
FYSFH4DMIPAM9R0.png

This project started as a desk clock idea and slowly turned into a way for me to teach and share what I know. I didn’t want this write-up to be just “here is the final result.” I wanted it to be something that actually helps people who are learning.

I know everyone will come to this project for different reasons. Some will be interested in the papercraft side, some only in the ESP32 + display side, and some might be curious about the full combination. So I explained everything step-by-step instead of skipping parts. The goal was that even a beginner should be able to understand how the paper model is made and how the electronics and UI work.

If you found something confusing or if you feel a step can be explained better, feel free to tell me — I’ll update it. And if you build your own version or make your own twist on this idea, I’d be really happy to see it.