Marcin Saj
Published © GPL3+

Flip-disc Binary Clock

Binary clock based on electromechanical flip-disc displays.

IntermediateWork in progress1 hour283
Flip-disc Binary Clock

Things used in this project

Hardware components

2x6 Flip-disc Display
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

2x6 Flip-disc Display Module

2x6 Flip-disc Display Module

2x6 Flip-disc Display Pinout

2x6 Flip-disc Display Pinout

2x6 Flip-disc Display Dimensions

2x6 Flip-disc Display Dimensions

2x6 Flip-disc Binary Clock Schematic

2x6 Flip-disc Binary Clock Schematic

Code

Flip-disc Binary Clock Code

Arduino
Flip-disc Binary Clock Code
/*-----------------------------------------------------------------------------------------------*
 * Flip-disc 2x6 Binary Clock by Marcin Saj https://flipo.io                                     *
 * https://github.com/marcinsaj/Flipo-Binary-Clock-2x6-Flip-Disc-Display                         *
 *                                                                                               *
 * Final Flip-disc Clock 4x7-Segment                                                             *
 * Setting Options:                                                                              *
 * - hour format 12/24                                                                           *
 * - time, hours & minutes                                                                       *
 *                                                                                               *
 * Attention!!! - Firmware update only for intermediate users!                                   *
 * The heart of the clock is the ATmega 328 microcontroller (default 16 MHz external crystal)    *
 * The clock code was written in the Arduino IDE and uses MiniCore                               *
 *                                                                                               *
 * If you want to upload your own code, there is an ISP connector on board for programming.      *
 * If you use a programmer that allows you to power the microcontroller to be programmed,        *
 * all you have to do is turn off (slide switch) the clock during programming.                   *
 * If the programmer (e.g. Atmel-ICE ISP) requires an additional power supply for                *
 * the microcontroller to be programmed, the clock controller board should be disconnected       *
 * from the 2x6 flip-disc display board during programming - this is very important because      *
 * the flip-disc display may be damaged during programming.                                      *
 *                                                                                               *
 * MiniCore - https://github.com/MCUdude/MiniCore                                                *
 * Clock schematic - https://bit.ly/BC-SCH                                                       *
 *-----------------------------------------------------------------------------------------------*/

#include <FlipDisc.h>         // https://github.com/marcinsaj/FlipDisc
#include <RTC_RX8025T.h>      // https://github.com/marcinsaj/RTC_RX8025T
#include <TimeLib.h>          // https://github.com/PaulStoffregen/Time
#include <Wire.h>             // https://arduino.cc/en/Reference/Wire (included with Arduino IDE)
#include <OneButton.h>        // https://github.com/mathertel/OneButton
#include <EEPROM.h>           // https://www.arduino.cc/en/Reference/EEPROM (included with Arduino IDE)


// Pin declaration for binary clock controller
#define EN_PIN  10
#define CH_PIN  A1 
#define PL_PIN  A0

// Buttons
#define B1_PIN A3  // Right button
#define B2_PIN A2  // Left button

// RTC
#define RTC_PIN 2 // RTC interrupt input

// Display rows
static const uint8_t TOP_ROW = 2;
static const uint8_t BOT_ROW = 1;

// Aliases for individual option settings
static const uint8_t HR12 = 0;   // Display time in 12 hour format
static const uint8_t HR24 = 1;   // Display time in 24 hour format

// "0" - all discs are cleared
// "63" - all discs are set
static const uint8_t CLR_ALL = 0;
static const uint8_t SET_ALL = 63;

// Declare structure that allows convenient access to the time elements:
// - tm.Hour - hours
// - tm.Minute - minutes
tmElements_t tm;

// Initialize a new OneButton instance for a buttons 
// BUTTON_PIN - Input pin for the button
// true - Button is active low
// true - Enable internal pull-up resistor
OneButton button1(B1_PIN, true, true);
OneButton button2(B2_PIN, true, true);

// Flags for storing button press status
bool shortPressButton1Status = false;
bool shortPressButton2Status = false;
bool longPressButton1Status = false;
bool longPressButton2Status = false;

// RTC interrupt flag
volatile bool interruptRtcStatus = false;

uint8_t binaryTopRowAR[6];
uint8_t binaryBotRowAR[6];

int hour_time = 0;
int minute_time = 0;
int time_hr = 0;

// Eeprom address where time format is stored
static const uint16_t ee_time_hr_address = 0;  // Time format 12/24 hour  

uint32_t flipDelay = 5;
bool settingsStatus = false;


// Interrupt from RTC
void rtcInterruptISR(void)
{
  interruptRtcStatus = true;
}

void setup() 
{
  Flip.Pin(EN_PIN, CH_PIN, PL_PIN);
  Flip.Init(D2X6);

  pinMode(RTC_PIN, INPUT_PULLUP);

  // RTC RX8025T initialization
  RTC_RX8025T.init();

  // Time update interrupt initialization. Interrupt generated by RTC (INT output): 
  // "INT_SECOND" - every second,
  // "INT_MINUTE" - every minute.
  RTC_RX8025T.initTUI(INT_MINUTE);

  // "INT_ON" - turn ON interrupt generated by RTC (INT output),
  // "INT_OFF" - turn OFF interrupt.
  RTC_RX8025T.statusTUI(INT_ON);
  
  // Assign an interrupt handler to the RTC output, 
  // an interrupt will be generated every minute to display the time
  attachInterrupt(digitalPinToInterrupt(RTC_PIN), rtcInterruptISR, FALLING);

  // Link the button functions
  button1.attachClick(ShortPressButton1);
  button2.attachClick(ShortPressButton2);
  button1.attachLongPressStart(LongPressButton1);
  // button2.attachLongPressStart(LongPressButton2);

  DelayTime(3000);

  // If the read values ​​are different from expected, set the time format to 12 hours.
  time_hr = EEPROM.read(ee_time_hr_address);
  if(time_hr != HR12 && time_hr != HR24) time_hr = HR12;  // Set the time display to 12 hour format

  DisplayTime();
}


void loop() 
{
  WatchButtons();
  if(interruptRtcStatus == true) DisplayTime();
  if(longPressButton1Status == true) SetTime();

  // Maybe for future settings
  // if(longPressButton2Status == true) DisplaySequence();
}

void DisplaySequence(void)
{
  // Maybe for future settings
}

void DisplayTime(void)
{
  GetRtcTime();

  DisplayData(BOT_ROW, minute_time);
  DisplayData(TOP_ROW, hour_time);

  interruptRtcStatus = false;
}


void GetRtcTime(void)
{
  // Get the time from the RTC and save it to the tm structure
  RTC_RX8025T.read(tm);

  hour_time = tm.Hour;
  minute_time = tm.Minute;

  // 12-Hour conversion
  if(time_hr == HR12)
  {
    if(hour_time > 12) hour_time = hour_time - 12;
    if(hour_time == 0) hour_time = 12; 
  }
}


void DisplayData(uint8_t row, uint8_t data)
{
  uint32_t waitTime = 0;
  
  if(settingsStatus == true) waitTime = 0;
  else waitTime = flipDelay;
  
  if(row == BOT_ROW)
  {
    DecToBinary(data, binaryBotRowAR);
    for(int i = 1; i <= 6 ; i++) 
    {
      Flip.Disc_2x6(1, i, binaryBotRowAR[i-1]);
      DelayTime(waitTime);
    }
  }

  if(row == TOP_ROW)
  {
    DecToBinary(data, binaryTopRowAR);
    for(int i = 1; i <= 6; i++) 
    {
      Flip.Disc_2x6(1, i+6, binaryTopRowAR[i-1]);
      DelayTime(waitTime);
    }
  }
}

// Time settings, 12/24 format, hours and minutes
void SetTime(void)
{
  ClearPressButtonFlags();
  settingsStatus = true;

  uint8_t time_settings_level = 1;
  bool updateDisplay = true;

  do // Stay in settings until all values are set
  {
    WatchButtons();

    if(shortPressButton1Status == true || shortPressButton2Status == true)
    {
      if(shortPressButton1Status == true)
      {
        if(time_settings_level == 2) time_hr++;
        if(time_settings_level == 4) hour_time++;
        if(time_settings_level == 6) minute_time++;
      }

      if(shortPressButton2Status == true)
      {
        if(time_settings_level == 2) time_hr--;
        if(time_settings_level == 4) hour_time--;
        if(time_settings_level == 6) minute_time--; 
      }
      
      ClearPressButtonFlags();
      updateDisplay = true;
    }

    if(longPressButton1Status == true)
    {      
      time_settings_level++;      
      if(time_settings_level >  6) time_settings_level = 0;  // Exit settings
      if(time_settings_level <= 6) 
      {
        updateDisplay = true;
      }

      ClearPressButtonFlags();
    }

    if(updateDisplay == true)
    {
      if(time_settings_level == 1)
      {
        DisplayData(TOP_ROW, SET_ALL);
        DisplayData(BOT_ROW, SET_ALL);
        DelayTime(1000);
        time_settings_level = 2;
      }

      if(time_settings_level == 2)
      {
        // HR12 = 0; HR24 = 1;
        if(time_hr > 1) time_hr = 0;
        if(time_hr < 0) time_hr = 1;
        if(time_hr == HR12) DisplayData(TOP_ROW, 12);
        if(time_hr == HR24) DisplayData(TOP_ROW, 24);
      }   

      if(time_settings_level == 3)
      {
        DisplayData(TOP_ROW, SET_ALL);
        DisplayData(BOT_ROW, SET_ALL);        
        DelayTime(1000);
        GetRtcTime();
        time_settings_level = 4;
      }

      if(time_settings_level == 4)
      {
        if(time_hr == HR12)
        {
          if(hour_time > 12) hour_time = 1;
          if(hour_time <= 0) hour_time = 12;
        }

        if(time_hr == HR24)
        {
          if(hour_time > 23) hour_time = 0;
          if(hour_time <  0) hour_time = 23;
        }
        
        DisplayData(TOP_ROW, hour_time);
      }

      if(time_settings_level == 5)
      {
        DisplayData(TOP_ROW, SET_ALL);
        DisplayData(BOT_ROW, SET_ALL);
        DelayTime(1000);
        time_settings_level = 6;
      }      

      if(time_settings_level == 6)
      {
        if(minute_time > 59) minute_time = 0;
        if(minute_time <  0) minute_time = 59;

        DisplayData(BOT_ROW, minute_time);
      }

      updateDisplay = false;
    }
  } while(time_settings_level > 0);

  // setTime(hh, mm, ss, day, month, year) 
  // The date is skipped and the seconds are set by default to 1
  // We are only interested in hours and minutes
  setTime(hour_time, minute_time, 0, 1, 1, 1);

  // Set the RTC from the system time
  RTC_RX8025T.set(now());

  EEPROM.write(ee_time_hr_address, time_hr);

  settingsStatus = false;
  DisplayTime();
}

// Better delay function
void DelayTime(uint32_t delayTime)
{
  uint32_t millis_time_now = millis();
  do
  { 
    // do nothing        
  } while(millis() - millis_time_now < delayTime);
}

// Converting decimal values ​​to binary
void DecToBinary(uint8_t decValue, uint8_t binaryArray[]) 
{
  for(int i = 0; i < 6; i++)
  {
    binaryArray[i] = decValue & B00000001;
    decValue = decValue >> 1;
  }
}

// Keep watching the buttons
void WatchButtons(void)
{
  button1.tick();
  button2.tick();
}

// Button flags clearing function
void ClearPressButtonFlags(void)
{
  shortPressButton1Status = false;
  shortPressButton2Status = false;
  longPressButton1Status = false;
  longPressButton2Status = false;
}

// Button press handling functions
void ShortPressButton1(void){shortPressButton1Status = true;}
void ShortPressButton2(void){shortPressButton2Status = true;}
void LongPressButton1(void){longPressButton1Status = true;}
void LongPressButton2(void){longPressButton2Status = true;}

Flip-disc Binary Clock Repository

Flip-disc Binary Clock Repository

Credits

Marcin Saj
28 projects • 29 followers
I am currently involved in the development of the Flipo.io and NixieTester.com project.
Contact

Comments

Please log in or sign up to comment.