PCBHub
November 24, 202563 min read 2 views

LED HOURGLASS USING ARDUINO : 8 Steps (with Pictures) - Instructables

pcbhub
pcbhub
Author
LED HOURGLASS USING ARDUINO : 8 Steps (with Pictures) - Instructables

Introduction: LED HOURGLASS USING ARDUINO

Article visual

In ancient times, people relied on an hourglass as a simple yet effective tool to measure the passage of time. The hourglass is made up of two glass bulbs connected by a narrow neck, through which sand flows steadily from the upper bulb to the lower one. The duration of the sand flow was usually fixed—often about an hour—which is why it came to be known as the hourglass. This timeless device symbolises the continuous flow of time, and even today it is admired for its simplicity and elegance. Inspired by this traditional concept, in this tutorial, we will recreate the same idea in a modern way by building a digital hourglass using Arduino, where electronic components will replace sand and glass to simulate the passage of time in a creative and interactive form.

Supplies

Article visual
Article visual
Article visual
  1. Arduino (Uno / Nano / Pro Mini) — 1
  2. ADXL335 breakout board — 1
  3. 8×8 LED matrix + MAX7219 module — 2
  4. 3.7V Li-ion battery — 1
  5. Charger+Booster Module — 1
  6. button switch - 1
  7. Wires
  8. 3D printed frame parts

Arduino (the brain)

The Arduino is the central controller for the digital hourglass: it reads the accelerometer, runs the “sand” animation, and talks to the LED drivers. I chose Arduino because it provides plenty of digital I/O, easy-to-use libraries, simple power and USB programming, and a huge community — perfect for a beginner-to-intermediate build. You can use an Uno, Nano, or a Pro Mini .

ADXL335 3-axis analog accelerometer (orientation sensor)

The ADXL335 gives you raw X, Y and Z analog voltages that correspond to the sensor’s orientation. For a digital hourglass, we just need to know which side is “up,” so the ADXL335 is a simple, low-cost choice that’s easy to read with analogRead() (No I²C required). It’s also tolerant of hobby-level wiring and works well mounted directly to the frame.

Two 8×8 LED matrices driven by MAX7219 (the sand displays)

To represent sand visually, we’ll use two 8×8 LED matrices — one representing the top bulb and one for the bottom. Each matrix is driven by a MAX7219 IC, so we only need three control pins (DIN, CLK, CS/LOAD) to control each module and can also daisy-chain them (data out of the first into the second). The MAX7219 handles multiplexing and brightness control for you, so you get crisp animations without manually time-multiplexing rows. You can animate falling grains by lighting pixels, moving them downward over time, and using the MAX7219 intensity register to tweak the visual “weight” of the sand.

3.7V Li-ion battery charger + 5V booster

A single-cell 3.7V Li-ion battery charger+ booster is a compact choice. Because the Arduino + MAX7219s usually want 5V. So we charge the battery and boost the voltage to 5v with the same module, such this way we can decrease the size

Step 1: How the Parts Work Together (conceptual)

  1. The ADXL335 senses which bulb is currently “up.”
  2. The Arduino reads the accelerometer, decides which matrix is the top and which is the bottom, and starts the sand animation accordingly.
  3. The MAX7219s receive frame data from the Arduino and show falling pixels. When you flip the hourglass, the Arduino detects the orientation change and reverses the animation, simulating sand moving from the new top matrix into the bottom matrix until one empties and the other fills


Step 2: CIRCUIT DIAGRAM

Article visual
Arduino → MAX7219 (first LED Matrix)
  1. DIN → D5
  2. CLK → D4
  3. CS/LOAD → D6
  4. VCC → 5V
  5. GND → GND
MAX7219 (first Matrix) → MAX7219 (second Matrix)
  1. DOUT → DIN of second matrix
  2. CLK → CLK
  3. CS/LOAD → CS/LOAD
  4. VCC → VCC
  5. GND → GND
ADXL335 → Arduino
  1. X → A1
  2. Y → A2
  3. VCC → 5V
  4. GND → GND

You can download the circuit diagram from here

Step 3: SOLDERING

Article visual
Article visual

I soldered the X and Y output pins of the ADXL335 accelerometer to the A1 and A2 analog pins of the Arduino Nano, while the VCC and GND pins of the sensor were connected to the 5V and GND pins of the Nano. The two 8×8 LED matrices with MAX7219 drivers were daisy-chained together, and I soldered their DIN, CLK, and CS pins to the Nano’s D5, D4, and D6 digital pins, respectively, along with their VCC and GND lines to 5V and GND. For the power supply, I connected a 3.7V Li-Po battery to the IP5306 base booster and charging module, and from there soldered the 5V and GND output pins of the module to the Arduino Nano to power the entire circuit.

Step 4: 3D Printed Encloure

Article visual
Article visual

I designed the enclosure using tinkercad and printed the files using my BambuLab A1 printer with PLA. You can download the files from here

Step 5: Arduino Programming

here i am explaig how the code works. by the way you can download complete code from here .

Included Libraries

Code
#include "Arduino.h"
#include "LedControl.h"     // Controls MAX7219 LED matrices
#include "Delay.h"          // Non-blocking delay helper

Matrix Indexes

Code
#define MATRIX_A 1
#define MATRIX_B 0

You have two MAX7219 matrices.

They are addressed as 0 and 1, named here for convenience.

Accelerometer thresholds

Code
#define ACC_THRESHOLD_LOW 300
#define ACC_THRESHOLD_HIGH 360

These values decide which direction the hourglass is tilted

(using analog readings from the accelerometer).

Pin assignments

Code
#define PIN_DATAIN 5
#define PIN_CLK 4
#define PIN_LOAD 6

#define PIN_X A1
#define PIN_Y A2

#define PIN_BUZZER 14

Pins for matrix, accelerometer, and buzzer.

Other settings

Code
#define ROTATION_OFFSET 90
#define DELAY_FRAME 100
#define DEBUG_OUTPUT 1
#define MODE_HOURGLASS 0

Global variables

Code
byte delayHours = 0;
byte delayMinutes = 1;
int mode = MODE_HOURGLASS;
int gravity;
LedControl lc = LedControl(PIN_DATAIN, PIN_CLK, PIN_LOAD, 2);
NonBlockDelay d;
int resetCounter = 0;
bool alarmWentOff = false;
  1. delayHours, delayMinutes – when a particle should fall
  2. gravity – current device orientation
  3. lc – LED matrix controller
  4. d – timing controller for particle drop
  5. alarmWentOff – prevents alarm from repeating

Drop timing

Code
long getDelayDrop() {
  return delayMinutes + delayHours * 60;
}

Returns the time (in seconds) between falling "sand" particles.

Debug Matrix Printer

Only used when DEBUG_OUTPUT == 1

Prints the full LED matrix contents on serial monitor in a grid format.

Helper coordinate functions

These convert (x,y) to adjacent positions:

Code
getDown()  // bottom-left
getLeft()
getRight()

Used for particle movement simulation.

Movement Permission Logic

These check whether a particle can move:

Code
bool canGoLeft()
bool canGoRight()
bool canGoDown()

Conditions include:

  1. Boundary check (edges)
  2. Whether the target LED is empty
  3. Whether sideways paths are clear

Movement functions

Actually move the particle by clearing old LED and lighting the new one:

Code
goDown()
goLeft()
goRight()

Counting particles

Code
int countParticles(int addr)

Counts lit LEDs on a matrix (top or bottom).

Useful to check when the top matrix becomes empty → trigger alarm.

Main particle simulation

Code
bool moveParticle(addr, x, y)
  1. Checks if LED (pixel) exists
  2. Randomly decides to move left or right if both possible
  3. Falls downward if possible
  4. Returns true if moved, otherwise false

This is the core sand movement physics.

Filling the hourglass

Code
void fill(addr, maxcount)

Fills the matrix in a diagonal (hourglass shape) pattern with a fixed number of pixels.

Gravity Detection

Code
int getGravity()

Reads accelerometer:

  1. 0° → upright
  2. 90° → left tilt
  3. 180° → upside down
  4. 270° → right tilt

Used to rotate matrices so the "sand" always falls correctly.

Direction-based matrix selection:

Code
int getTopMatrix()
int getBottomMatrix()

Decides which matrix is "top" based on tilt.

Reset hourglass

Code
void resetTime()
  1. Clears both matrices
  2. Fills the top matrix with 60 particles
  3. Resets the drop timer

Update matrix (physics engine)

Code
bool updateMatrix()

Runs the particle movement algorithm:

  1. Sweeps through all pixels diagonally
  2. Attempts movement on both matrices
  3. Randomizes left/right sweep direction
  4. Returns true if any particle moved

Drop particle between matrices

Code
boolean dropParticle()

When the timer expires:

  1. Drops one pixel from the top to bottom
  2. Makes beep when particle drops
  3. Handles two orientations: 0° & 180°
  4. Returns true if a pixel was transferred

Alarm

Code
void alarm()

When the top matrix becomes empty:

  1. Plays 5 beep sounds, 1 second apart

Setup

Code
void setup() {
  Serial.begin(9600);
  randomSeed(analogRead(A0));

  // init matrices
  for each matrix:
    lc.shutdown(false);
    lc.setIntensity(1);

  resetTime();
}

Main loop

Code
void loop() {
  delay(DELAY_FRAME);

  gravity = getGravity();
  lc.setRotation((ROTATION_OFFSET + gravity) % 360);

  bool moved = updateMatrix();
  bool dropped = dropParticle();

  // Trigger alarm when top is empty
  if (!moved && !dropped && !alarmWentOff && countParticles(getTopMatrix()) == 0) {
    alarmWentOff = true;
    alarm();
  }

  // Reset alarm after flipping
  if (dropped) alarmWentOff = false;
}



Step 6: ASSEMBLY

Article visual
Article visual

After uploading the code, I placed the circuit inside the 3D enclosure and finished the project

Step 7: Final Look

Article visual
Article visual
Article visual
Article visual
Article visual

Step 8: Video Tutorial

Enjoyed this article?

Support the author by leaving a like.