Digital Hours Glass

by niharikalokhand246 in Circuits > Arduino

3 Views, 0 Favorites, 0 Comments

Digital Hours Glass

project 2.jpg
project.jpg

Hello everyone my name is Niharika and my teammate Vedant we made this project. We were so exited to join our first contest in instructable. This is an interesting project this project.I saw this project on youtube short video then I search about this project on youtube.

This is the link of the video we refered from Youtube:- https://www.youtube.com/watch?v=yDBFaEyJ6ME

Supplies

WhatsApp Image 2025-07-04 at 3.33.02 PM.jpeg

1 LED Matrix 8*8

What is LED Matrix

Especially using an Arduino and an 8*8 LED matrix (commonly driven by a MAX7219 chip). You can create simple games like Tetris, Snake, Pong or Maze games.


2 gyro (MPU 6050, accelerometer)

What is MPU 6050

The MPU-6050 is a MEMS (micro-electro-mechanical system)sensor that combines a 3-axis gyroscope and a 3-axis accelerometer in a single chip. Its commonly used in electronics projects robotics,drones and wearable technology for motion tracking and orientation detection.

3. Arduino Nano

What is Arduino Nano

The Arduino Nano is a compact, breadboard friendly microcontroller board based on the ATmega328P (the same chip is used in the Arduino Uno). Its designed for projects that need a small,versatille and cost-effective development board.


note: To fix my arduino i use PCB (printed circuit board)

PCB:- provide pathways made of copper that allow electrical current flow between different component

4. BMS

What is BMS

A Battery Management System is an essential electronic system performance of rechargeable batteries, especially in electric vehicals (EVS), Solar power systems, laptops and portable devices.

5. switch

6. 3 lithium battery 3.7v

7. jumper wire

8. wire stripper

9. wire cutter

10. soldering iron

11. iron wire

12. glue gun

13. scissors

14. cutter

15. pencil

16. eraser

17. scale

Circuit

WhatsApp Image 2025-07-04 at 12.28.27 PM.jpeg
Untitled presentation.jpg
WhatsApp Image 2025-07-01 at 12.09.27 PM (6).jpeg
WhatsApp Image 2025-07-01 at 12.09.27 PM (7).jpeg

The circuit we make on goggle doc also you can use to make circuit on paint

THE CONNECTION

  1. LED matrix to Arduino Nano

VCC connect to 5v on the Arduino

GND connect to GND on Arduino

DIN connect to D5 on Arduino

CS connect to D6 on Arduino

CLK connect to D4 on Arduino

  1. MPU 6065 (Accelerometer) to Arduino Nano

VCC connect to 5v on the Arduino

GND connect to GND on Arduino

SCL connect to A5 on arduino

SDA connect to A4 on arduino

  1. BMS to arduino nano, switch and lithium battery

out- connect to GND on arduino

out+ connect to switch output

B- connect to battery negative

B+ connect to battery positive

  1. switch to Arduino Nano

input connect to vin pin on Arduino

I face so many problem my all connection are to week my jumper dose not fix well i make stick them with the help of glue gun

the material I use is sun-board and it is little difficult to cut them perfect measurement



We first check my MPU with the help of below code we use two MPU 6050 first MPU is short because to wrong connection.

Them we check second MPU make sure connection is correct and check on serial monitor.

#include<Wire.h>

#include<MPU6050.h>


MPU6050 mpu;


void setup() {

Serial.begin(9600);

Wire.begin();


Serial.println("Initializing MPU6050...");

mpu.initialize();


if (mpu.testConnection()) {

Serial.println("MPU6050 connection successful!");

} else {

Serial.println("MPU6050 connection failed!");

while (1); // Stop here if connection fails

}

}


void loop() {

int16_t ax, ay, az;

int16_t gx, gy, gz;


// Read raw accelerometer and gyroscope data

mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);


Serial.print("Accel (X,Y,Z): ");

Serial.print(ax); Serial.print(" ");

Serial.print(ay); Serial.print(" ");

Serial.print(az); Serial.print(" | ");


Serial.print("Gyro (X,Y,Z): ");

Serial.print(gx); Serial.print(" ");

Serial.print(gy); Serial.print(" ");

Serial.println(gz);


delay(500); // Update rate

}

Then we check 8/8 matrix i use four LED matrix because two short because of wrong soldering



The code i use

#include "Arduino.h"

#include <MPU6050_tockn.h>

#include "LedControl.h"

#include "Delay.h"


#define MATRIX_A 1

#define MATRIX_B 0


MPU6050 mpu6050(Wire);


// Values are 260/330/400

#define ACC_THRESHOLD_LOW -25

#define ACC_THRESHOLD_HIGH 25


// Matrix

#define PIN_DATAIN 5

#define PIN_CLK 4

#define PIN_LOAD 6


// Accelerometer

#define PIN_X mpu6050.getAngleX()

#define PIN_Y mpu6050.getAngleY()


// Rotary Encoder

#define PIN_ENC_1 3

#define PIN_ENC_2 2

#define PIN_ENC_BUTTON 7


#define PIN_BUZZER 14


// This takes into account how the matrixes are mounted

#define ROTATION_OFFSET 90


// in milliseconds

#define DEBOUNCE_THRESHOLD 500


#define DELAY_FRAME 100


#define DEBUG_OUTPUT 1


#define MODE_HOURGLASS 0

#define MODE_SETMINUTES 1

#define MODE_SETHOURS 2


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;



/**

* Get delay between particle drops (in seconds)

*/

long getDelayDrop() {

// since we have exactly 60 particles we don't have to multiply by 60 and then divide by the number of particles again :)

return delayMinutes + delayHours * 60;

}



#if DEBUG_OUTPUT

void printmatrix() {

Serial.println(" 0123-4567 ");

for (int y = 0; y<8; y++) {

if (y == 4) {

Serial.println("|----|----|");

}

Serial.print(y);

for (int x = 0; x<8; x++) {

if (x == 4) {

Serial.print("|");

}

Serial.print(lc.getXY(0,x,y) ? "X" :" ");

}

Serial.println("|");

}

Serial.println("-----------");

}

#endif




coord getDown(int x, int y) {

coord xy;

xy.x = x-1;

xy.y = y+1;

return xy;

}

coord getLeft(int x, int y) {

coord xy;

xy.x = x-1;

xy.y = y;

return xy;

}

coord getRight(int x, int y) {

coord xy;

xy.x = x;

xy.y = y+1;

return xy;

}




bool canGoLeft(int addr, int x, int y) {

if (x == 0) return false; // not available

return !lc.getXY(addr, getLeft(x, y)); // you can go there if this is empty

}

bool canGoRight(int addr, int x, int y) {

if (y == 7) return false; // not available

return !lc.getXY(addr, getRight(x, y)); // you can go there if this is empty

}

bool canGoDown(int addr, int x, int y) {

if (y == 7) return false; // not available

if (x == 0) return false; // not available

if (!canGoLeft(addr, x, y)) return false;

if (!canGoRight(addr, x, y)) return false;

return !lc.getXY(addr, getDown(x, y)); // you can go there if this is empty

}




void goDown(int addr, int x, int y) {

lc.setXY(addr, x, y, false);

lc.setXY(addr, getDown(x,y), true);

}

void goLeft(int addr, int x, int y) {

lc.setXY(addr, x, y, false);

lc.setXY(addr, getLeft(x,y), true);

}

void goRight(int addr, int x, int y) {

lc.setXY(addr, x, y, false);

lc.setXY(addr, getRight(x,y), true);

}



int countParticles(int addr) {

int c = 0;

for (byte y=0; y<8; y++) {

for (byte x=0; x<8; x++) {

if (lc.getXY(addr, x, y)) {

c++;

}

}

}

return c;

}



bool moveParticle(int addr, int x, int y) {

if (!lc.getXY(addr,x,y)) {

return false;

}


bool can_GoLeft = canGoLeft(addr, x, y);

bool can_GoRight = canGoRight(addr, x, y);


if (!can_GoLeft && !can_GoRight) {

return false; // we're stuck

}


bool can_GoDown = canGoDown(addr, x, y);


if (can_GoDown) {

goDown(addr, x, y);

} else if (can_GoLeft&& !can_GoRight) {

goLeft(addr, x, y);

} else if (can_GoRight && !can_GoLeft) {

goRight(addr, x, y);

} else if (random(2) == 1) { // we can go left and right, but not down

goLeft(addr, x, y);

} else {

goRight(addr, x, y);

}

return true;

}




void fill(int addr, int maxcount) {

int n = 8;

byte x,y;

int count = 0;

for (byte slice = 0; slice < 2*n-1; ++slice) {

byte z = slice<n ? 0 : slice-n + 1;

for (byte j = z; j <= slice-z; ++j) {

y = 7-j;

x = (slice-j);

lc.setXY(addr, x, y, (++count <= maxcount));

}

}

}




/**

* Detect orientation using the accelerometer

*

* | up | right | left | down |

* --------------------------------

* 400 | | | y | x |

* 330 | y | x | x | y |

* 260 | x | y | | |

*/

int getGravity() {

int x = mpu6050.getAngleX();

int y = mpu6050.getAngleY();

if (y < ACC_THRESHOLD_LOW) { return 90; }

if (x > ACC_THRESHOLD_HIGH) { return 0; }

if (y > ACC_THRESHOLD_HIGH) { return 270; }

if (x < ACC_THRESHOLD_LOW) { return 180; }

}



int getTopMatrix() {

return (getGravity() == 90) ? MATRIX_A : MATRIX_B;

}

int getBottomMatrix() {

return (getGravity() != 90) ? MATRIX_A : MATRIX_B;

}




void resetTime() {

for (byte i=0; i<2; i++) {

lc.clearDisplay(i);

}

fill(getTopMatrix(), 60);

d.Delay(getDelayDrop() * 1000);

}




/**

* Traverse matrix and check if particles need to be moved

*/

bool updateMatrix() {

int n = 8;

bool somethingMoved = false;

byte x,y;

bool direction;

for (byte slice = 0; slice < 2*n-1; ++slice) {

direction = (random(2) == 1); // randomize if we scan from left to right or from right to left, so the grain doesn't always fall the same direction

byte z = slice<n ? 0 : slice-n + 1;

for (byte j = z; j <= slice-z; ++j) {

y = direction ? (7-j) : (7-(slice-j));

x = direction ? (slice-j) : j;

// for (byte d=0; d<2; d++) { lc.invertXY(0, x, y); delay(50); }

if (moveParticle(MATRIX_B, x, y)) {

somethingMoved = true;

};

if (moveParticle(MATRIX_A, x, y)) {

somethingMoved = true;

}

}

}

return somethingMoved;

}




/**

* Let a particle go from one matrix to the other

*/

boolean dropParticle() {

if (d.Timeout()) {

d.Delay(getDelayDrop() * 1000);

if (gravity == 0 || gravity == 180) {

if ((lc.getRawXY(MATRIX_A, 0, 0) && !lc.getRawXY(MATRIX_B, 7, 7)) ||

(!lc.getRawXY(MATRIX_A, 0, 0) && lc.getRawXY(MATRIX_B, 7, 7))

) {

// for (byte d=0; d<8; d++) { lc.invertXY(0, 0, 7); delay(50); }

lc.invertRawXY(MATRIX_A, 0, 0);

lc.invertRawXY(MATRIX_B, 7, 7);

tone(PIN_BUZZER, 440, 10);

return true;

}

}

}

return false;

}




void alarm() {

for (int i=0; i<5; i++) {

tone(PIN_BUZZER, 440, 200);

delay(1000);

}

}




void resetCheck() {

int z = analogRead(A3);

if (z > ACC_THRESHOLD_HIGH || z < ACC_THRESHOLD_LOW) {

resetCounter++;

Serial.println(resetCounter);

} else {

resetCounter = 0;

}

if (resetCounter > 20) {

Serial.println("RESET!");

resetTime();

resetCounter = 0;

}

}




void displayLetter(char letter, int matrix) {

// Serial.print("Letter: ");

// Serial.println(letter);

lc.clearDisplay(matrix);

lc.setXY(matrix, 1,4, true);

lc.setXY(matrix, 2,3, true);

lc.setXY(matrix, 3,2, true);

lc.setXY(matrix, 4,1, true);


lc.setXY(matrix, 3,6, true);

lc.setXY(matrix, 4,5, true);

lc.setXY(matrix, 5,4, true);

lc.setXY(matrix, 6,3, true);


if (letter == 'M') {

lc.setXY(matrix, 4,2, true);

lc.setXY(matrix, 4,3, true);

lc.setXY(matrix, 5,3, true);

}

if (letter == 'H') {

lc.setXY(matrix, 3,3, true);

lc.setXY(matrix, 4,4, true);

}

}




void renderSetMinutes() {

fill(getTopMatrix(), delayMinutes);

displayLetter('M', getBottomMatrix());

}

void renderSetHours() {

fill(getTopMatrix(), delayHours);

displayLetter('H', getBottomMatrix());

}





void knobClockwise() {

Serial.println("Clockwise");

if (mode == MODE_SETHOURS) {

delayHours = constrain(delayHours+1, 0, 64);

renderSetHours();

} else if(mode == MODE_SETMINUTES) {

delayMinutes = constrain(delayMinutes+1, 0, 64);

renderSetMinutes();

}

Serial.print("Delay: ");

Serial.println(getDelayDrop());

}

void knobCounterClockwise() {

Serial.println("Counterclockwise");

if (mode == MODE_SETHOURS) {

delayHours = constrain(delayHours-1, 0, 64);

renderSetHours();

} else if (mode == MODE_SETMINUTES) {

delayMinutes = constrain(delayMinutes-1, 0, 64);

renderSetMinutes();

}

Serial.print("Delay: ");

Serial.println(getDelayDrop());

}




volatile int lastEncoded = 0;

volatile long encoderValue = 0;

long lastencoderValue = 0;

long lastValue = 0;

void updateEncoder() {

int MSB = digitalRead(PIN_ENC_1); //MSB = most significant bit

int LSB = digitalRead(PIN_ENC_2); //LSB = least significant bit


int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number

int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value


if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue--;

if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue++;


// Serial.print("Value: ");

// Serial.println(encoderValue);

if ((encoderValue % 4) == 0) {

int value = encoderValue / 4;

if (value > lastValue) knobClockwise();

if (value < lastValue) knobCounterClockwise();

lastValue = value;

}

lastEncoded = encoded; //store this value for next time

}




/**

* Button callback (incl. software debouncer)

* This switches between the modes (normal, set minutes, set hours)

*/

volatile unsigned long lastButtonPushMillis;

void buttonPush() {

if((long)(millis() - lastButtonPushMillis) >= DEBOUNCE_THRESHOLD) {

mode = (mode+1) % 3;

Serial.print("Switched mode to: ");

Serial.println(mode);

lastButtonPushMillis = millis();


if (mode == MODE_SETMINUTES) {

lc.backup(); // we only need to back when switching from MODE_HOURGLASS->MODE_SETMINUTES

renderSetMinutes();

}

if (mode == MODE_SETHOURS) {

renderSetHours();

}

if (mode == MODE_HOURGLASS) {

lc.clearDisplay(0);

lc.clearDisplay(1);

lc.restore();

resetTime();

}

}

}




/**

* Setup

*/

void setup() {

mpu6050.calcGyroOffsets(true);

// Serial.begin(9600);

mpu6050.begin();


// while (!Serial) {

// ; // wait for serial port to connect. Needed for native USB

// }


// setup rotary encoder

pinMode(PIN_ENC_1, INPUT);

pinMode(PIN_ENC_2, INPUT);

pinMode(PIN_ENC_BUTTON, INPUT);

digitalWrite(PIN_ENC_1, HIGH); //turn pullup resistor on

digitalWrite(PIN_ENC_2, HIGH); //turn pullup resistor on

digitalWrite(PIN_ENC_BUTTON, HIGH); //turn pullup resistor on

attachInterrupt(digitalPinToInterrupt(PIN_ENC_1), updateEncoder, CHANGE);

attachInterrupt(digitalPinToInterrupt(PIN_ENC_2), updateEncoder, CHANGE);

attachInterrupt(digitalPinToInterrupt(PIN_ENC_BUTTON), buttonPush, RISING);


// Serial.println(digitalPinToInterrupt(PIN_ENC_1));

// Serial.println(digitalPinToInterrupt(PIN_ENC_2));

// Serial.println(digitalPinToInterrupt(PIN_ENC_BUTTON));


randomSeed(analogRead(A0));


// init displays

for (byte i=0; i<2; i++) {

lc.shutdown(i,false);

lc.setIntensity(i,0);

}


resetTime();

}




/**

* Main loop

*/

void loop() {

mpu6050.update();

Serial.println("angleX : ");

Serial.println(mpu6050.getAngleX());

Serial.println("\tangleY : ");

Serial.println(mpu6050.getAngleY());


delay(DELAY_FRAME);



// update the driver's rotation setting. For the rest of the code we pretend "down" is still 0,0 and "up" is 7,7

gravity = getGravity();

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


// handle special modes

if (mode == MODE_SETMINUTES) {

renderSetMinutes(); return;

} else if (mode == MODE_SETHOURS) {

renderSetHours(); return;

}


// resetCheck(); // reset now happens when pushing a button

bool moved = updateMatrix();

bool dropped = dropParticle();


// alarm when everything is in the bottom part

if (!moved && !dropped && !alarmWentOff && (countParticles(getTopMatrix()) == 0)) {

alarmWentOff = true;

alarm();

}

// reset alarm flag next time a particle was dropped

if (dropped) {

alarmWentOff = false;

}

}