Project tutorial

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!

  • 6,554 views
  • 1 comment
  • 23 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

There are some additional libraries that you need to install before compile the main code of this project:

  • <SoftWire.h> // Similar to Wire.h library to run with Multi-Function Shield
  • <Time.h> // Time library
  • <TimeLib.h>
  • <MsTimer2.h> // Timer library to read the push buttons

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 this case.

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:

/*           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 

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 pressure to advance the hours quickly.
  • Center button: set the minutes with the same left button operation pattern.
  • Right button: a quick press to move between the time and temperature display.

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 in the mirror the time (or temperature) numbers in the correct way and it does not matter if the clock is upside down, in the left or right position!

The magic of this is driven by accelerometers that identify the positioning of the clock in three dimensions and provide information to display the numbers compensating for the mirror effect.

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

Digital Clock with LED Display Mirrored (Published)Arduino
Arduino's code for Mirrored Clock
/*           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

Development Board for AtTiny MCU

by Vincenzo G.

  • 197 views
  • 0 comments
  • 6 respects

Version 2.0 Advanced Attendance System (Without Ethernet)

Project tutorial by GadgetProgrammers

  • 3,237 views
  • 5 comments
  • 46 respects

The Magnetic Field and RGB Tester

Project tutorial by Kutluhan Aktar

  • 498 views
  • 0 comments
  • 6 respects

How To Use DS18B20 Water Proof Temperature Sensor

Project showcase by IoTBoys

  • 291 views
  • 0 comments
  • 4 respects

Control LED Using Your Voice Command

by IoTBoys

  • 1,253 views
  • 1 comment
  • 12 respects
Add projectSign up / Login