Project showcase

Digital Clock with Mirrored Display Driven by Accelerometers © CC BY-NC-SA

This is an amazing digital clock with automatic cancellation of the mirroring effect! Just move it in front of a mirror and be amazed!

  • 14,365 views
  • 1 comment
  • 38 respects

Components and supplies

Apps and online services

About this project

1 - Introduction

This project is an original application of a Digital Clock with Mirrored Display and driven by Arduino and accelerometers.

With this clock it is possible to place it in front of a mirror and the image of its LED display will be shown in the correct position, automatically cancelling the mirroring effect!

This principle is the same as that used in HUD (head-up display) for mirror surfaces or glass, such as on car windshields and aircrafts.

Video

2 - Material

This is the material list you need:

  • 01 x Arduino UNO R3
  • 01 x Multi Funtion Shield (MFD) for Arduino
  • 01 x MPU-6050 breakout
  • 04 x Female Jumper Wires

Multi Function Shield (MFD)

The use of MFD can simplify and speed up your prototypes development. There are many features available in this shield:

  • 4 digit 7-segment LED display module driven by two serial 74HC595's
  • 4 x surface mount LED's in a parallel configuration
  • 10K adjustable precision potentiometer
  • 3 x independent push buttons
  • Piezo buzzer
  • DS18B20 temperature sensor interface
  • LM35 temperature sensor interface
  • Infrared receiver interface
  • Serial interface header for convenient connection to serial modules such as Bluetooth, wireless interface, voice module, a voice recognition module, etc.

Note: Before use it I recommend to get more information at Cohesive Computing.

MPU-6050 breakout

The MPU-6050 breakout consists in a triple axis accelerometer and gyroscope plus a temperature sensor with following specifications:

  • Chip: MPU-6050
  • Input Voltage: 3-5V
  • ADC: 16 bits
  • I/O: standard I2C
  • Gyroscope full-scale range: ±250, 500, 1000, 2000°/s
  • Accelerometer full-scale range: ±2, ±4, ±8, ±16g
  • Temperature sensor range: -40 to +85 ºC

3 - Assembly

The assembly is very simple using only a few extra components due to Multi Function Shield for Arduino.

  • Insert the Multi Shield into the Arduino.
  • Fasten the MPU-6050 breakout on the Arduino board using a tiny screw as shown in the pictures.

There only 04 jumper wires to be connected:

  • Red wire: Vcc (+5V)
  • Black wire: Gnd
  • Green wire: SCL of MPU-6050 breakout to port #6 in the Multi Shield.
  • Yellow wire: SDA of MPU-6050 breakout to port #5 in the Multi Shield.

4 - Coding Tips

There is an additional library you need:

  • <SoftWire.h> // Similar to Wire.h library to run with Multi-Function Shield

The SoftWire.h library is an alternative to run with MFD because the original Wire.h library of Arduino can use only the ports A4 (SDA) and A5 (SCL) for I2C communications and these ports are not available in Multi-Function Shield.

With SoftWire.h is possible to define new ports for I2C communication protocol that must be in the first lines of the code as shown bellow:

Important Notes:

  • Instead of to use some specific library to control de MFD, I developed my own code to manage the display with mirrored digit numbers.
  • All read data of MPU-6050 are between -16384 and +16384.
  • The easiest way to compute the accelerometers data is to convert them in the range of degrees from -90º to +90º using "map" function.
  • The same is for the range of temperature from -40ºC to 85ºC, but in this case I have included too a correction factor of +10 as shown below:
 AcX = map(AcX, -16384, 16384, -90, 90); 
 AcY = map(AcY, -16384, 16384, -90, 90); 
 AcZ = map(AcZ, -16384, 16384, -90, 90); 
 Temp = map(Temp, -16384, 16384, -40, 85) + 10;   // Personal correction factor: +10 

5 - Configuration

There are 3 buttons to set the clock after activating it:

  • Left button: press to adjust the hours. Quickly press to set up step by step. Continuous press to advance the hours quickly.
  • Center button: set the minutes with the same left button operation pattern.
  • Right button: a quick press to move for temperature mode.

Note: At temperature mode is possible to change to Fahrenheit or Celsius mode pressing the Left button.

Note: In Temperature Mode is possible to change the status to Fahrenheit or Celsius pressing the Left button.

6 - Have a fun

Now it's time to experience something unusual.

Try placing your digital watch or your cell phone showing the time in front of a mirror. What you see are the mirror images according to the physical laws of optics.

If you repeat the experiment using the Digital Clock of this project, you will see at mirror the time (or temperature) numbers in correct positioning and does not matter if the the clock is upside down or not!

The magic of this is driven by accelerometers that identify the positioning of the clock in three dimensions and provide correct information to the display.

Something about Plane Mirrors:

  • The image distance equals the object distance.
  • The image is unmagnified.
  • The image is virtual.
  • The image is not inverted.
  • Left and right are reversed.

Based on these facts, the code works to interpret two situations:

  • The clock is standing: the LED display numbers are reversed from left to right.
  • The clock is upside down: the LED display numbers are inverted but not reversed.

With this you can read the LED display in the mirror in any position you put the clock.

Code

Mirrored_Clock_Using_Multi_Shield_V1_5__Published_.inoArduino
Arduino's code
/*           Mirrored Clock with Multi Shield
                       by LAGSILVA
                  Rev 1.5 - 14.Out.2017
*/

#define SDA_PORT PORTD
#define SDA_PIN 5
#define SCL_PORT PORTD
#define SCL_PIN 6

#include <SoftWire.h>
#define MPU 0x68                        // I2C address for MPU6050
SoftWire Wire = SoftWire();

#include <Time.h>                       // Time library
#include <TimeLib.h>
#include <MsTimer2.h>                   // Timer library

// Module connection pins (Digital Pins)
#define LATCH_PIN 4        // Arduino UNO conection on Pin #4  = Latch of Display Module
#define CLK_PIN 7          // Arduino UNO conection on Pin #7  = Clock of Display Module
#define DATA_PIN 8         // Arduino UNO conection on Pin #8  = Data of Display Module

#define POT_PIN A0
#define BTN_1_PIN A1
#define BTN_2_PIN A2
#define BTN_3_PIN A3

//  No Mirror:         Front Mirror:        Rear Mirror:
//      A                    D                   A
//     ----                 ----                ----
//  F |    | B           E |    | C          B |    | F
//     -G -                 -G -                -G -
//  E |    | C           F |    | B          C |    | E
//     ----                 ----                ----
//      D                    A                  D
//

//   Chars Code
byte N[] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6, 0xC6, 0x9C };

int hora, minuto, brightness;
int unidadeHora, unidadeMinuto, dezenaHora, dezenaMinuto, delayRefresh;
byte mirror, tempStatus;
unsigned long ti;
boolean ajustaHora = true, ajustaMinuto = true, mostraTemperatura = true;


//Variaveis para armazenar valores dos sensores
int AcX, AcY, AcZ, Temp;


void setup() {

  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLK_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);

  pinMode(POT_PIN, INPUT);
  pinMode(BTN_1_PIN, INPUT);
  pinMode(BTN_2_PIN, INPUT);
  pinMode(BTN_3_PIN, INPUT);

  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);

  MsTimer2::set(120, kbdRead);
  MsTimer2::start();

  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);

  //Inicializa o MPU-6050
  Wire.write(0);
  Wire.endTransmission(true);

  mirror = 1;
  tempStatus = 0;

}


void loop() {

  delayRefresh = 1000;

  // Brightness of Display (Min = 1 : Max = 20);
  brightness = constrain(map(analogRead(POT_PIN), 100, 200, 0, 20), 1, 20);

  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);

  //Solicita os dados do sensor
  Wire.requestFrom(MPU, 14, true);

  //Armazena o valor dos sensores nas variaveis correspondentes
  AcX = Wire.read() << 8 | Wire.read(); //0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); //0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); //0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Temp = Wire.read() << 8 | Wire.read(); //0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)

  AcX = map(AcX, -16384, 16384, -90, 90);
  AcY = map(AcY, -16384, 16384, -90, 90);
  AcZ = map(AcZ, -16384, 16384, -90, 90);
  Temp = map(Temp, -16384, 16384, -40, 85) + 10;                // Personal correction factor: +10

  if (-AcY >= 30 && AcZ >= 30) {
    mirror = 1;
  }

  if ((-AcY <= 30 && AcZ >= 30) | (-AcY <= 30 && AcZ < 30)) {
    mirror = 2;
  }

  if (-AcY >= 30 & AcZ <= 30) {
    mirror = 3;
  }

  hora = hour();
  minuto = minute();
  unidadeHora = hora % 10;
  dezenaHora = hora / 10;
  unidadeMinuto = minuto % 10;
  dezenaMinuto = minuto / 10;

  if (tempStatus == 0) {                                                        // Display only the Time (HH:MM)

    ti = millis();                                                              // Initial time for blinking dot

    while ((millis() - ti) < delayRefresh) {                                    // Timer in miliseconds to refresh the display

      if (mirror < 3) {                                                         // No Mirror (1) or Frontal Mirror (2) mode
        displayChars(N[dezenaHora], mirror, 0);                                 // Display Digit 0
        displayChars(N[unidadeHora], mirror, 1);                                // Display Digit 1
        displayChars(N[dezenaMinuto], mirror, 2);                               // Display Digit 2
        displayChars(N[unidadeMinuto], mirror, 3);                              // Display Digit 3
        displayChars(0, 1, 3);                                                  // Clear Digit 3 (last digit)
      }

      if (mirror == 3) {                                                        // Rear Mirror (3) mode
        displayChars(N[dezenaHora], mirror, 3);                                 // Display Digit 3
        displayChars(N[unidadeHora], mirror, 2);                                // Display Digit 2
        displayChars(N[dezenaMinuto], mirror, 1);                               // Display Digit 1
        displayChars(N[unidadeMinuto], mirror, 0);                              // Display Digit 0
        displayChars(0, 1, 0);                                                  // Clear Digit 0 (last digit)
      }

      if (millis() - ti < delayRefresh / 2) {
        displayChars(1, 1, 1);                                                  // Display Dot at Digit 1
        displayChars(0, 1, 1);                                                  // Clear Dot (Blinking Effect)

        if (millis() - ti > delayRefresh) {
          ti = millis();
        }

      }

      delay(brightness);                                                        // Brightness control

    }

  }

  if (tempStatus == 1) {                                                        // Display only the Temperature (C)

    ti = millis();                                                              // Initial time for blinking dot

    while ((millis() - ti) < delayRefresh / 8) {                                // Timer in miliseconds to refresh the display

      if (mirror < 3) {                                                         // No Mirror (1) or Frontal Mirror (2) mode
        displayChars(N[Temp / 10], mirror, 0);                                  // Display tenth of Temperature
        displayChars(N[Temp % 10], mirror, 1);                                  // Display unit of Temperature
        displayChars(N[10], mirror, 2);                                         // Display symbol of Degrees
        displayChars(N[11], mirror, 3);                                         // Display "C" of Celsius Degrees
        displayChars(0, 1, 3);                                                  // Clear Digit 3 (last digit)
      }

      if (mirror == 3) {                                                        // Rear Mirror (3) mode
        displayChars(N[Temp / 10], mirror, 3);                                  // Display tenth of Temperature
        displayChars(N[Temp % 10], mirror, 2);                                  // Display unit of Temperature
        displayChars(N[10], mirror, 1);                                         // Display symbol of Degrees
        displayChars(N[11], mirror, 0);                                         // Display "C" of Celsius Degrees
        displayChars(0, 1, 0);                                                  // Clear Digit 0 (last digit)
      }

      delay(brightness);                                                        // Brightness control

    }

  }

}


void displayChars(byte num, byte statusM, byte disp) {

  byte k, charsM = 0;

  if (statusM == 1) {                                                           // No Mirror
    charsM = num;
  }

  if (statusM == 2) {                                                           // Frontal Mirror
    for (k = 0; k <= 7; k++) {
      charsM = bitWrite(charsM, 7 - k, bitRead(num, k));
    }
    charsM = (charsM << 4 | (charsM << 2) >> 4 & 12 ) | ((charsM & 64) >> 5);
  }

  if (statusM == 3) {                                                           // Rear Mirror
    for (k = 0; k <= 7; k++) {
      charsM = bitWrite(charsM, 7 - k, bitRead(num, k));
    }
    charsM = (charsM << 7 | (charsM << 1) & 124 ) | ((charsM & 64) >> 5);
  }

  digitalWrite(LATCH_PIN, LOW);
  shiftOut(DATA_PIN, CLK_PIN, LSBFIRST, ~charsM);                               // Display number
  shiftOut(DATA_PIN, CLK_PIN, LSBFIRST, 128 >> disp);                           // Set Digit (0-1-2-3)
  digitalWrite(LATCH_PIN, HIGH);

}


void kbdRead() {

  ajustaHora = digitalRead(BTN_1_PIN);
  ajustaMinuto = digitalRead(BTN_2_PIN);
  mostraTemperatura = digitalRead(BTN_3_PIN);

  if (!ajustaHora) {
    adjustTime(3600);
    delayRefresh = 150;
  }

  if (!ajustaMinuto) {
    adjustTime(60);
    delayRefresh = 150;
  }

  if (!mostraTemperatura) {
    tempStatus = (tempStatus + 1) % 2;
  }

}

Comments

Similar projects you might like

(Updated) Digital Clock w/ Mirrored Display for UNO-R3 & 101

Project tutorial by LAGSILVA

  • 993 views
  • 0 comments
  • 3 respects

Digital & Binary Clock In 8 Digits x 7 Segments LED Display

Project showcase by LAGSILVA

  • 1,827 views
  • 2 comments
  • 9 respects

Digital Clock with Arduino, RTC and Shift Register 74HC595

Project tutorial by LAGSILVA

  • 20,727 views
  • 16 comments
  • 46 respects

Analog Clock with LED Matrix and Arduino

Project tutorial by LAGSILVA

  • 12,237 views
  • 8 comments
  • 39 respects

4-Stroke Digital Clock With Arduino

Project showcase by LAGSILVA

  • 11,103 views
  • 9 comments
  • 39 respects

Tri-Mode Digital Clock With ATtiny85 And RTC

Project showcase by LAGSILVA

  • 5,207 views
  • 0 comments
  • 19 respects
Add projectSign up / Login