Mechanical 7-segment Clock

I saw this design on Thingiverse a few weeks ago.  I believe it was also featured in Make Magazine's blog.  Mechanical 7-segment Display, simple and smooth

It's such a cool design that I had to make a clock out of it.  Note that the designer, Shinsaku Hiura, also has a full clock made from another of his designs. 

I printed out 4 copies of the design using Hatchbox and Overture PLA.  Instead of painting just the faces of the moving arms I chose to print the entire part in white.  The movement is controlled by 4x 9-gram servos.  I had a leftover Arduino Nano to control the system.  The time is read from a DS3231 real-time clock module and a momentary pushbutton switch is used to switch between the clock and the date. 

The switch triggers an interrupt in the code and makes the clock change the display to the date (MM / DD) format for 5 seconds before reverting back to the time in 24-hour format.  The clock is powered by a 5V USB charger.  Note that the servo power comes directly from the power supply and not through the Arduino 5V voltage regulator.

Future code updates will allow for setting the clock time with the pushbutton switch instead of attaching to the computer and uploading fresh code.

Arduino Code:

/*
 7-Segment Mechanical Clock

 (c) 2021 by Brian Jones
 Uses 4x 7-segment mechanical clock prints
   Thingiverse: https://www.thingiverse.com/thing:5066794
   by Shinsaku Hiura (https://www.thingiverse.com/shiura)

 Base code to read DS3231 module from:
   DS3231: Real-Time Clock. Simple example
    Read more: www.jarzebski.pl/arduino/komponenty/zegar-czasu-rzeczywistego-rtc-ds3231.html
    GIT: https://github.com/jarzebski/Arduino-DS3231
    Web: http://www.jarzebski.pl
    (c) 2014 by Korneliusz Jarzebski
*/

#include <Wire.h>
#include <DS3231.h>
#include <VarSpeedServo.h>

#define BUTTON_PIN 3
#define LED_PIN 13

// used to calibrate the servo trims
#define CALIBRATION_MODE false
int calibrate_num = 0;

DS3231 clock;
RTCDateTime dt;

volatile bool clock_date = LOW;
volatile int clock_min;


// An array of servos for the clock
VarSpeedServo clockServo[4];
// mapping the servo to IO pins
byte servoPin[4] = {9, 10, 11, 12};

// Min and max PWM signals for servos.
int servoMin[4] = { 600,  700,  800,  800};
int servoMax[4] = {2200, 2200, 2300, 2300};
// offset value for each servo
int servoTrim[4] = {-80, 0, 25, 0};

 

void setup() {
  Serial.begin(9600);

  // Initialize DS3231
  Serial.println("Initialize DS3231");;
  clock.begin();

  // Set sketch compiling time -- uncomment to set the time and then comment out so the time isn't reset each time the Arduino boots.
  // Future versions will have code to set the time with the pushbutton switch
  //  clock.setDateTime(__DATE__, __TIME__);

  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), myISR, RISING); // trigger when button pressed, but not when released.
}


void loop() {
  dt = clock.getDateTime();

  if (CALIBRATION_MODE) {
    Serial.print("Calibrating...");
    Serial.println(calibrate_num);

    calibrateMode();
  }
  else {
    // For leading zero look to DS3231_dateformat example
    Serial.print("Raw data: ");
    Serial.print(dt.year);   Serial.print("-");
    Serial.print(dt.month);  Serial.print("-");
    Serial.print(dt.day);    Serial.print(" ");
    Serial.print(dt.hour);   Serial.print(":");
    Serial.print(dt.minute); Serial.print(":");
    Serial.print(dt.second); Serial.println("");
  
    if (clock_date == HIGH) {
      display_date(dt.month, dt.day);
      display_clock(dt.hour, dt.minute);
    }
    else if (dt.minute != clock_min) {
      clock_min = dt.minute;
      display_clock(dt.hour, dt.minute);
    }
  }
  
  delay(1000);
}


void display_clock(int c_hour, int c_minute) {
  Serial.print("Servo Clock data:  S1: ");
  Serial.print((c_hour / 10) % 10); Serial.print(" S2: ");
  Serial.print(c_hour % 10);  Serial.print(" - S3: ");
  Serial.print((c_minute / 10) % 10);    Serial.print(" S4: ");
  Serial.print(c_minute %10);   Serial.println("");

  int loc[4] = { (c_hour / 10) % 10, c_hour % 10, (c_minute / 10) % 10, c_minute % 10 };
  moveServo( loc );
}


void display_date(int c_month, int c_day) {
  Serial.print("Servo Date data:  S1: ");
  Serial.print((c_month / 10) % 10); Serial.print(" S2: ");
  Serial.print(c_month % 10);  Serial.print(" - S3: ");
  Serial.print((c_day / 10) % 10);    Serial.print(" S4: ");
  Serial.print(c_day %10);   Serial.println("");

  int loc[4] = { (c_month / 10) % 10, c_month % 10, (c_day / 10) % 10, c_day % 10 };
  moveServo( loc );
  
  clock_date = LOW;
  delay(5000);
}


void moveServo( int (&loc)[4] ) {
  for (byte i = 0; i < 4; i++) {
    clockServo[i].attach(servoPin[i], servoMin[i], servoMax[i]);
  }

  for (byte i = 0; i < 4; i++) {
    clockServo[i].write(constrain(convert_time_to_servo(loc[i], servoMin[i], servoMax[i]) + servoTrim[i], servoMin[i], servoMax[i]), 50, false);
  }

  for (byte i = 0; i < 4; i++) {
    clockServo[i].wait();
  }

  for (byte i = 0; i < 4; i++) {
    clockServo[i].detach();
  }
}


int convert_time_to_servo (int t_num, int servoMin, int servoMax) {
  return (map(t_num, 0, 9, servoMax, servoMin));
}


void calibrateMode() {
  Serial.println("Calibrate MODE");
  Serial.print("calibrate move servo ... ");
  Serial.println(calibrate_num);
  
  int loc[4] = {calibrate_num, calibrate_num, calibrate_num, calibrate_num};
  moveServo( loc );
}



void myISR() {
  clock_date = !clock_date;
  calibrate_num++;
  if (calibrate_num > 9) {
    calibrate_num = 0;
  }
}

Comments

Popular posts from this blog

V-22 Osprey Project - Flight Control

V-22 Osprey Project - Design Changes for v3

V-22 Osprey Project - Tuning and Setup