Skip to content

jvalver1/hourglass_instructables

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⏳ Arduino Nano LED Hourglass

A sand-hourglass simulator built on two daisy-chained MAX7219 8×8 LED matrices.
Each lit LED is a grain of sand that falls, slides, and settles under real gravity — detected by a GY-521 / MPU6050 I2C accelerometer. Flip the device and the sand flows the other way.

Hourglass Animation


✨ Features

Feature Details
Sand Physics Diagonal fall → lateral slide → random drift (no directional bias)
Gravity Sensing GY-521 / MPU6050 accelerometer detects 4 cardinal orientations (0°, 90°, 180°, 270°)
Auto-Rotation Display rotates with the device — sand always falls "downward"
Configurable Timer Set hours + minutes in include/config.h; default is 1 minute
Non-Blocking Loop Custom NonBlockDelay class keeps animation running while timing events
Debug Mode Optional Serial ASCII dump of the matrix state (9600 baud)

🛠️ Hardware

Components

# Component Notes
1 Arduino Nano ATmega328P (old or new bootloader)
2 MAX7219 8×8 LED Matrix (×2) Daisy-chained; common modules available
3 GY-521 Accelerometer Module MPU6050 I2C accelerometer; default address 0x68
4 Jumper Wires
5 Breadboard / PCB

🔌 Pinout

Arduino Nano → MAX7219 LED Matrices

Arduino Nano          MAX7219 (Module 1 — near end)
──────────────        ──────────────────────────────
D5  (PIN_DATAIN)  ──► DIN   (Data In)
D4  (PIN_CLK)     ──► CLK   (Clock)
D6  (PIN_LOAD)    ──► CS/LOAD (Chip Select)
5V                ──► VCC
GND               ──► GND

MAX7219 Module 1       MAX7219 (Module 2 — far end)
─────────────          ────────────────────────────
DOUT           ──────► DIN
CLK       )     ──────► SCL
5V            ──────► VCC
GND           ──────► GND

Note: The code uses the default MPU6050 I2C address 0x68, which is used when the GY-521 AD0 pin is low or unconnected. If you wire AD0 high, update MPU_ADDR in include/config.h to 0x69.

Complete Pinout Summary Table

Arduino Nano Pin Direction Connected To Purpose
D4 OUT MAX7219 CLK SPI clock signal
D5 OUT MAX7219 DIN SPI data to LED matrices
D6 OUT MAX7219 LOAD SPI chip select
A0 IN Floating (no connection) randomSeed() at startup only
A4 / SDA IN/OUT GY-521 SDA I2C data
A5 / SCL OUT GY-521 SCL I2C clock
5V OUT GY-521 VCC Module power
GND GY-521 GND Common ground

📐 Wiring Diagram

                          ┌─────────────┐
                     ┌────┤ MAX7219 #1  │
 Arduino Nano        │    │   (near)    │
 ┌──────────┐        │    └──┬──────────┘
 │ D5 ──────┼──DATA──┘   DOUT│
 │ D4 ──────┼──CLK───────────┤ CLK
 │ D6 ──────┼──CS────────────┤ CS
 │ 5V ──────┼────────────────┤ VCC    ┌─────────────┐
 │ GND──────┼────────────────┤ GND    │ MAX7219 #2  │
 │          │           DOUT─┼──DIN──►│   (far)     │
 │ A4 ──────┼──SDA───────────┤        └─────────────┘
 │ A5 ──────┼──SCL──┐  (GY-521 / MPU6050)
 │ 5V ──────┼──VCC──┘
 │ GND──────┼──GND
 │          │
 └──────────┘

📁 Project Structure

hourglass_instructables/
├── platformio.ini          # PlatformIO project configuration (board, framework)
├── include/
│   └── config.h            # ⚙️  All user-configurable parameters
├── src/
│   └── main.cpp            # Main sketch — physics engine, setup/loop
├── lib/
│   ├── LedControl/
│   │   ├── LedControl.h    # MAX7219 driver — API & documentation
│   │   └── LedControl.cpp  # MAX7219 driver — implementation
│   └── Delay/
│       ├── Delay.h         # Non-blocking delay — API & documentation
│       └── Delay.cpp       # Non-blocking delay — implementation
├── backup/                 # 📦 Original Arduino IDE flat-file backup
│   ├── hourglass.ino
│   ├── LedControl.h / .cpp
│   └── Delay.h / .cpp
└── README.md

⚙️ Configuration

All user-tunable settings are in include/config.h.
No other file needs to be edited for common adjustments.

Timer Duration

#define TIMER_HOURS    0   // Add hours to the countdown
#define TIMER_MINUTES  1   // Add minutes to the countdown

The total drain time = (TIMER_HOURS × TIMER_MINUTES_PER_HOUR + TIMER_MINUTES) minutes.
Each of the SAND_PARTICLE_COUNT sand particles is scheduled using TIMER_MILLISECONDS_PER_SECOND.

Examples:

TIMER_HOURS TIMER_MINUTES Total Duration
0 1 1 minute
0 5 5 minutes
0 30 30 minutes
1 0 1 hour

Display & Animation

#define ROTATION_OFFSET  90   // Physical matrix mounting offset (degrees)
#define DELAY_FRAME      100  // Animation frame period (ms)
#define DISPLAY_INTENSITY 1   // MAX7219 LED brightness, 0-15

Accelerometer Thresholds

#define MPU_ADDR  0x68  // Default GY-521 / MPU6050 I2C address

#define ACC_THRESHOLD_LOW   -10000  // Raw MPU6050 value below this = axis tilted "low"
#define ACC_THRESHOLD_HIGH   10000  // Raw MPU6050 value above this = axis tilted "high"

The MPU6050 defaults to its +/-2g accelerometer range, where -1g is roughly -16384 and +1g is roughly +16384. Adjust the thresholds if orientation detection is too sensitive or not sensitive enough for your module and mounting.

Debug Output

#define DEBUG_OUTPUT  1   // 1 = enable Serial ASCII matrix dump, 0 = disable

Connect to Serial monitor at SERIAL_BAUD_RATE to see the matrix state printed as ASCII art.


🚀 Build & Upload (PlatformIO)

Prerequisites

Steps

# 1. Open the project folder in VS Code
# 2. PlatformIO will auto-detect platformio.ini and install the toolchain

# Build only (verify compilation)
pio run

# Build and upload to connected Arduino Nano
pio run --target upload

# Open Serial monitor (SERIAL_BAUD_RATE, default 9600 baud)
pio device monitor

Bootloader note: The platformio.ini targets nanoatmega328 (old bootloader).
If your Nano uses the new bootloader, change the board to nanoatmega328new.


🧠 How It Works

Sand Physics

Each loop iteration calls updateMatrix(), which sweeps every cell of the 8×8 grid in a randomised diagonal order and applies the following priority rules to each particle:

1. Can fall diagonally (both L and R cells free)?  → fall diagonally
2. Only left is free?                              → slide left
3. Only right is free?                             → slide right
4. Both free but diagonal blocked?                 → drift randomly L or R
5. Both blocked?                                   → particle settled, no move

The randomised sweep direction per stripe prevents particles from always drifting the same way, producing a natural-looking simulation.

Gravity Detection

getGravity() reads the GY-521 / MPU6050 over I2C. It requests the X and Y acceleration registers in a single transaction, then maps the raw signed values to the nearest cardinal angle:

Y-axis low  → 0°   (upright)
X-axis high → 90°  (rotated right)
Y-axis high → 180° (upside-down)
X-axis low  → 270° (rotated left)

The dead-band between ACC_THRESHOLD_LOW and ACC_THRESHOLD_HIGH prevents jitter near boundaries. With the default wiring, the sensor uses address 0x68; wiring AD0 high changes it to 0x69.

Particle Drop (Hourglass Neck)

dropParticle() fires once per timer period and transfers one grain from the raw corner cell [0,0] of MATRIX_A to raw corner cell [7,7] of MATRIX_B — the physical junction between the two daisy-chained displays.

Display Rotation

The LedControl library maintains a rotation state. Every frame, loop() calls:

lc.setRotation((ROTATION_OFFSET + gravity) % GRAVITY_FULL_ROTATION_DEGREES);

All subsequent LED coordinate writes are automatically rotated, so the user always sees "down" as down, regardless of which way the device is held.


🧾 Recent Cleanup

This version matches the current hardware build and keeps the code easier to tune:

  • Removed all passive buzzer, tick, and alarm code because the current hardware does not include a buzzer.
  • Removed the blocking end-of-timer alarm path, so the animation loop no longer pauses for alarm beeps.
  • Moved repeated hardware, display, timer, gravity, matrix, random, and serial values into include/config.h.
  • Cached the gravity reading once per loop and reused that value when choosing the top and bottom matrices.
  • Seeded the cached gravity value once during setup() before the initial hourglass fill.
  • Cleaned the README and code comments so text is stored and displayed as valid UTF-8.
  • Verified the project still builds successfully with pio run.

📜 Licence

The LedControl library is based on the original work by Eberhard Fahle (© 2007) and is distributed under the MIT Licence.

The hourglass application code (main.cpp, Delay.*, config.h) is released under the MIT Licence.


🙏 Credits

  • LedControl library — Eberhard Fahle (original MIT-licensed MAX7219 driver)
  • Hourglass concept — Based on the Instructables LED hourglass project
  • PlatformIO migration & documentation — This repository

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors