Mirko Pavleski
Published © GPL3+

3D-Printed Kinetic Servo Clock

Unusual 3D-printed clock controlled by two servo motors.

IntermediateFull instructions provided3 hours2,753
3D-Printed Kinetic Servo Clock

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×2

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

3D parts

Schematics

schematic

Code

code

C/C++
/* =============================================================
    Module kinetic servo clock - copyright aeropic 2016

    version du 161103
    lines marked with <<<<<<<<<<<<<<<<<<< are the places where you can modify...

   
   ============================================================ */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <TimeLib.h>
#include <WiFiUdp.h>
#include <Servo.h>

// delete or mark the next line as comment if you don't need these
//#define CALIBRATION // enable calibration mode   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// When in calibration mode, adjust the following factor until the servos move exactly 90 degrees
#define SERVOFAKTORLEFT 1.10  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#define SERVOFAKTORRIGHT 1.10//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

// Zero-position of left and right servo
// When in calibration mode, adjust the NULL-values so that the servo arms are horizontal/vertical
#define SERVOLEFTNULL 48  // expressed in degrees (between 40 and 55)  <<<<<<<<<<<<<<<<<<<<<
#define SERVORIGHTNULL 53                                             // <<<<<<<<<<<<<<<<<<<<

// clock geometry
#define S 50  // length of servo arm
#define D 70  // distance between orgin and servo arm joint
#define R 41  // clock radius
#define L 85  //length of clock arms
#define OFFSET 14  // offset between servo line and clock perimeter (distance between X axis and 6h on Y axis)
// center of clock Y coordinate = R + OFFSET

Servo myservo1;  // create servo object to control a servo
Servo myservo2;  // create servo object to control a servo


#include <ESP8266HTTPUpdateServer.h>  // OTA web update

const char* host = "esp8266-webupdate"; // OTA web update


const char* ssid = "WiFi SSID";  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
const char* password = "wi-fi password"; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


// NTP Servers:
// IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
 IPAddress timeServer(129, 6, 15, 28); // time-b.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
const int timeZone = 1;     // Central European Time
WiFiUDP Udp;
unsigned int localPort = 2390;  // local port to listen for UDP packets

uint8_t MAC_array[6];
char MAC_char[18];

ESP8266WebServer httpServer(85);  // OTA web update on port 85 
ESP8266HTTPUpdateServer httpUpdater; // OTA web update

ESP8266WebServer server(80);

const int servo1 = 12;
const int servo2 = 13;

time_t madate;
int previousMinute = 0; // store the last position of the minutes arm

/*----------------------------------------------------------------------*
 * Inlined modified Arduino Timezone Library v1.0                       *                *
 * Jack Christensen Mar 2012                                            *                              *
 *----------------------------------------------------------------------*/

// inlined Timezone.h
//convenient constants for dstRules
enum week_t {Last, First, Second, Third, Fourth}; 
enum dow_t {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat};
enum month_t {Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};

//structure to describe rules for when daylight/summer time begins,
//or when standard time begins.
struct TimeChangeRule
{
    char abbrev[6];    //five chars max
    uint8_t week;      //First, Second, Third, Fourth, or Last week of the month
    uint8_t dow;       //day of week, 1=Sun, 2=Mon, ... 7=Sat
    uint8_t month;     //1=Jan, 2=Feb, ... 12=Dec
    uint8_t hour;      //0-23
    int offset;        //offset from UTC in minutes
};
        
class Timezone
{
    public:
        Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart);
        Timezone(int address);
        time_t toLocal(time_t utc);
        time_t toLocal(time_t utc, TimeChangeRule **tcr);
        time_t toUTC(time_t local);
        boolean utcIsDST(time_t utc);
        boolean locIsDST(time_t local);
        void readRules(int address);
        void writeRules(int address);

    private:
        void calcTimeChanges(int yr);
        time_t toTime_t(TimeChangeRule r, int yr);
        TimeChangeRule _dst;    //rule for start of dst or summer time for any year
        TimeChangeRule _std;    //rule for start of standard time for any year
        time_t _dstUTC;         //dst start for given/current year, given in UTC
        time_t _stdUTC;         //std time start for given/current year, given in UTC
        time_t _dstLoc;         //dst start for given/current year, given in local time
        time_t _stdLoc;         //std time start for given/current year, given in local time
};


// inlined Timezone.cpp (modified to suppress AVR EEPROM not compatible with ESP8266

/*----------------------------------------------------------------------*
 * Create a Timezone object from the given time change rules.           *
 *----------------------------------------------------------------------*/
Timezone::Timezone(TimeChangeRule dstStart, TimeChangeRule stdStart)
{
    _dst = dstStart;
    _std = stdStart;
}


/*----------------------------------------------------------------------*
 * Convert the given UTC time to local time, standard or                *
 * daylight time, as appropriate.                                       *
 *----------------------------------------------------------------------*/
time_t Timezone::toLocal(time_t utc)
{
    //recalculate the time change points if needed
    if (year(utc) != year(_dstUTC)) calcTimeChanges(year(utc));

    if (utcIsDST(utc))
        return utc + _dst.offset * SECS_PER_MIN;
    else
        return utc + _std.offset * SECS_PER_MIN;
}

/*----------------------------------------------------------------------*
 * Convert the given UTC time to local time, standard or                *
 * daylight time, as appropriate, and return a pointer to the time      *
 * change rule used to do the conversion. The caller must take care     *
 * not to alter this rule.                                              *
 *----------------------------------------------------------------------*/
time_t Timezone::toLocal(time_t utc, TimeChangeRule **tcr)
{
    //recalculate the time change points if needed
    if (year(utc) != year(_dstUTC)) calcTimeChanges(year(utc));

    if (utcIsDST(utc)) {
        *tcr = &_dst;
        return utc + _dst.offset * SECS_PER_MIN;
    }
    else {
        *tcr = &_std;
        return utc + _std.offset * SECS_PER_MIN;
    }
}

/*----------------------------------------------------------------------*
 * Convert the given local time to UTC time.                            *
 *                                                                      *
 * WARNING:                                                             *
 * This function is provided for completeness, but should seldom be     *
 * needed and should be used sparingly and carefully.                   *
 * It is used to set time at compile time                               *
 *                                                                      *
 * Ambiguous situations occur after the Standard-to-DST and the         *
 * DST-to-Standard time transitions. When changing to DST, there is     *
 * one hour of local time that does not exist, since the clock moves    *
 * forward one hour. Similarly, when changing to standard time, there   *
 * is one hour of local times that occur twice since the clock moves    *
 * back one hour.                                                       *
 *                                                                      *
 * This function does not test whether it is passed an erroneous time   *
 * value during the Local -> DST transition that does not exist.        *
 * If passed such a time, an incorrect UTC time value will be returned. *
 *                                                                      *                                               *
 *----------------------------------------------------------------------*/
time_t Timezone::toUTC(time_t local)
{
    //recalculate the time change points if needed
    if (year(local) != year(_dstLoc)) calcTimeChanges(year(local));

    if (locIsDST(local))
        return local - _dst.offset * SECS_PER_MIN;
    else
        return local - _std.offset * SECS_PER_MIN;
}

/*----------------------------------------------------------------------*
 * Determine whether the given UTC time_t is within the DST interval    *
 * or the Standard time interval.                                       *
 *----------------------------------------------------------------------*/
boolean Timezone::utcIsDST(time_t utc)
{
    //recalculate the time change points if needed
    if (year(utc) != year(_dstUTC)) calcTimeChanges(year(utc));

    if (_stdUTC > _dstUTC)    //northern hemisphere
        return (utc >= _dstUTC && utc < _stdUTC);
    else                      //southern hemisphere
        return !(utc >= _stdUTC && utc < _dstUTC);
}

/*----------------------------------------------------------------------*
 * Determine whether the given Local time_t is within the DST interval  *
 * or the Standard time interval.                                       *
 *----------------------------------------------------------------------*/
boolean Timezone::locIsDST(time_t local)
{
    //recalculate the time change points if needed
    if (year(local) != year(_dstLoc)) calcTimeChanges(year(local));

    if (_stdLoc > _dstLoc)    //northern hemisphere
        return (local >= _dstLoc && local < _stdLoc);
    else                      //southern hemisphere
        return !(local >= _stdLoc && local < _dstLoc);
}

/*----------------------------------------------------------------------*
 * Calculate the DST and standard time change points for the given      *
 * given year as local and UTC time_t values.                           *
 *----------------------------------------------------------------------*/
void Timezone::calcTimeChanges(int yr)
{
    _dstLoc = toTime_t(_dst, yr);
    _stdLoc = toTime_t(_std, yr);
    _dstUTC = _dstLoc - _std.offset * SECS_PER_MIN;
    _stdUTC = _stdLoc - _dst.offset * SECS_PER_MIN;
}

/*----------------------------------------------------------------------*
 * Convert the given DST change rule to a time_t value                  *
 * for the given year.                                                  *
 *----------------------------------------------------------------------*/
time_t Timezone::toTime_t(TimeChangeRule r, int yr)
{
    tmElements_t tm;
    time_t t;
    uint8_t m, w;            //temp copies of r.month and r.week

    m = r.month;
    w = r.week;
    if (w == 0) {            //Last week = 0
        if (++m > 12) {      //for "Last", go to the next month
            m = 1;
            yr++;
        }
        w = 1;               //and treat as first week of next month, subtract 7 days later
    }

    tm.Hour = r.hour;
    tm.Minute = 0;
    tm.Second = 0;
    tm.Day = 1;
    tm.Month = m;
    tm.Year = yr - 1970;
    t = makeTime(tm);        //first day of the month, or first day of next month for "Last" rules
    t += (7 * (w - 1) + (r.dow - weekday(t) + 7) % 7) * SECS_PER_DAY;
    if (r.week == 0) t -= 7 * SECS_PER_DAY;    //back up a week if this is a "Last" rule
    return t;
}

// end of inlined library


/* how to define a time zone : Define a TimeChangeRule as follows:
 * ==============================================================

TimeChangeRule myRule = {abbrev, week, dow, month, hour, offset};

Where:
abbrev is a character string abbreviation for the time zone; it must be no longer than five characters.
week is the week of the month that the rule starts.
dow is the day of the week that the rule starts.
hour is the hour in local time that the rule starts (0-23).
offset is the UTC offset in minutes for the time zone being defined.

For convenience, the following symbolic names can be used:
week: First, Second, Third, Fourth, Last
dow: Sun, Mon, Tue, Wed, Thu, Fri, Sat
month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec

For the Eastern US time zone, the TimeChangeRules could be defined as follows:
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  //UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   //UTC - 5 hours

For a time zone that does not change to daylight/summer time, pass the same rule twice to the constructor, for example:
Timezone usAZ(usMST, usMST);
 */

// comment/uncomment here after the time zone definition (2 lines) adapted to your location  <<<<<<<<<<<<<<<<<<<

//US Eastern Time Zone (New York, Detroit)
//TimeChangeRule myDST = {"EDT", Second, Sun, Mar, 2, -240};    //Daylight time = UTC - 4 hours
//TimeChangeRule mySTD = {"EST", First, Sun, Nov, 2, -300};     //Standard time = UTC - 5 hours

//Central European Time Zone (Paris)
TimeChangeRule myDST = {"CEST", Second, Sun, Mar, 2, 120};    //Daylight summer time = UTC + 2 hours
TimeChangeRule mySTD = {"CET", Last, Sun, Oct, 2, 60};     //Standard time = UTC + 1 hours

Timezone myTZ(myDST, mySTD);
TimeChangeRule *tcr;        //pointer to the time change rule, use to get TZ abbrev





/*-------- NTP code ----------*/
// ============================

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

// time management routines
//=========================




time_t getNtpTime()
{
  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  sendNTPpacket(timeServer);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      Serial.print("date NTP : jour : ");
    //  Serial.println(secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR);
    //  return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
       Serial.println(secsSince1900 - 2208988800UL );
      return secsSince1900 - 2208988800UL ;
    }
  }
  Serial.println("No NTP Response");
  return 0; // return 0 if unable to get the time
}




// formule des angles du triangle
// ==============================
double triangle_angle(double a, double b, double c) {
  // cosine rule for angle between c and a
  Serial.print(" triangle abc ") ;
  Serial.print(a);
  Serial.print(" ");
  Serial.print(b);
  Serial.print(" ");
  Serial.print(c);
  Serial.println(" ");
  Serial.println( 180 * (a * a + c * c - b * b) / (2 * a * c) / PI);
  return acos((a * a + c * c - b * b) / (2 * a * c));
}


// positionne l'articulation des bras sur le point de coordonnnées Tx Ty
// =====================================================================
//
// origine des coordonnées au centre de l'horloge de rayon R
// OFFSET = distance entre axe des servos et bas de l'horloge (chiffre 6 h)
//
void set_XY(double Tx, double Ty)
{
  delay(1);
  double dx, dy, c, a1, a2;

  // calculate triangle between pen, servoLeft and arm joint
  // cartesian dx/dy
  dx = Tx + D;
  dy = Ty + OFFSET + R;

  Serial.print("dx ") ;
  Serial.print(dx) ;
  Serial.print(" dy ") ;
  Serial.print(dy);

  // polar lemgth (c) and angle (a1)
  c = sqrt(dx * dx + dy * dy); //
  a1 = atan2(dy, dx); //
  a2 = triangle_angle(S, L, c);

  Serial.print("a1 ") ;
  Serial.print(a1 * 180 / PI) ;
  Serial.print(" a2 ") ;
  Serial.print(a2 * 180 / PI);
  Serial.print(" c ") ;
  Serial.println(c);

  //  myservo1.writeMicroseconds(floor(((a1 - a2 ) * SERVOFAKTORLEFT) + SERVOLEFTNULL));
  Serial.print("A1 ") ;
  Serial.print((a1 - a2) * 180 / PI);
  servo1Angle((a1 - a2) * 180 / PI);

  // calculate triangle between pen joint, servoRight and arm joint
  dx = D - Tx;
  dy = Ty + OFFSET + R;

  c = sqrt(dx * dx + dy * dy);
  a1 = atan2(dy, dx);
  a2 = triangle_angle(S, L, c);

  Serial.print(" A2 ") ;
  Serial.print("a1 ") ;
  Serial.print(a1 * 180 / PI) ;
  Serial.print(" a2 ") ;
  Serial.print(a2 * 180 / PI);
  Serial.print(" c ") ;
  Serial.println(c);

  servo2Angle((a1 - a2) * 180 / PI);
}

// deplace le servo 1 de l'angle alpha (positif =  sens trigo, 0 = bras horizontal)
void servo1Angle (double alpha)
{
  myservo1.write((SERVOLEFTNULL - alpha)*SERVOFAKTORLEFT);
}

// deplace le servo 2 de l'angle alpha (positif =  sens antitrigo, 0 = bras horizontal à gauche)
void servo2Angle (double alpha)
{
  myservo2.write((180 - SERVORIGHTNULL + alpha)*SERVOFAKTORRIGHT);
}


// positionne l'aiguille des minutes sur le point "minu"
// =======================================================
//
// origine des coordonnées au centre de l'horloge de rayon R

//
void gotoMinute(int minu,int dr)
{
  double X, Y;
  X = (R+dr) * cos((-(6 * minu) - 270) * PI / 180);
  Y = (R+dr) * sin ((-(6 * minu) - 270) * PI / 180);
  set_XY(X, Y) ;
  delay(60);
}

// rotate the clock  clockwise from deb N steps. dr is a delta added to clock radius
void circleFromToCW(int deb, int N, int dr)
{
  int i;
  for (int i = deb ; i < deb + N; i++)
  {
    gotoMinute (i,dr) ;
  }
}

// rotate the clock anti clockwise from deb N steps. dr is a delta added to clock radius
void circleFromToAW(int deb, int N, int dr)
{
  int i;
  for (int i = 0 ; i < N; i++)
  {
    gotoMinute (deb - i, dr) ;
  }
}

// setup
// ======
void setup(void) {

  // set the two servo pins as input
  pinMode(servo1, INPUT);
  pinMode(servo2, INPUT);

  Serial.begin(115200);

  // wifi stuff
  // ==========
  WiFi.disconnect();
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("servo clock");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("servo clock");

  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  //OTA routines
  httpUpdater.setup(&httpServer);  // OTA web update
  httpServer.begin();              // OTA web update
  MDNS.addService("http", "tcp", 85); // OTA web update
  Serial.printf("HTTPUpdateServer ready! Open http://addIP:85/update in your browser\n");  // OTA web update



  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }


  // get mac addres
  WiFi.macAddress(MAC_array);

  for (int i = 0; i < sizeof(MAC_array); ++i) {
    sprintf(MAC_char, "%s%02x:", MAC_char, MAC_array[i]);
  }
  Serial.println();
  Serial.print("MAC ADDRESS: ");
  Serial.println(MAC_char);
  Serial.println();

  //init pour synchro temps
  // Serial.println("Starting UDP");
  Udp.begin(localPort);
  //  Serial.print("Local port: ");
  //  Serial.println(Udp.localPort());
  //  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);


  //init servo position
  myservo1.attach(servo1);  // attaches the servo on pin 9 to the servo object
  myservo2.attach(servo2);  // attaches the servo on pin 9 to the servo object
  servo1Angle(-90);
  servo2Angle(-90);
  delay (5000);
  
#ifdef CALIBRATION
#else
  //set clock time
  madate =  myTZ.toLocal(now(), &tcr);
 
  
  gotoMinute(30,0);
  circleFromToCW(30, 90,0) ; //goto noon
  delay(1000);

  circleFromToCW(0, (5*(hour(madate)%12)), 0); // set the hours arm pushing clockwise
  delay(1000);
  
  // escape maneuver
  gotoMinute ((5*(hour(madate)%12))-2, 0);
  delay(500);
  gotoMinute ((5*(hour(madate)%12))-2, -6);
  delay(500);
  gotoMinute ((5*(hour(madate)%12))-2, -12);
  delay(500);
  gotoMinute ((5*(hour(madate)%12))+1, -12);
  delay(500);
  gotoMinute ((5*(hour(madate)%12)), 0);
  
  circleFromToAW((5*(hour(madate)%12))+1,2+(120 + (5*(hour(madate)%12)) - minute(madate))%60, 0); //set the minutes arm to the correct time (anticlockwise)
  delay(1000);
  
  circleFromToCW(minute(madate)+1, (120 +30- minute(madate))%60, 0); // set the arms to parking 
  servo1Angle(-90);
  servo2Angle(-90);
  previousMinute = minute(madate);
 #endif
   
}



//boucle principale
//=================
void loop(void) {

  double X, Y;

  httpServer.handleClient();  // OTA web update


#ifdef CALIBRATION
  servo1Angle(0);
  servo2Angle(0);
  delay (5000);

  servo1Angle(-90);
  servo2Angle(-90);
  delay (5000);

#else

  // set time if needed
  // ==================
madate =  myTZ.toLocal(now(), &tcr);

int entry = 30;
if (minute(madate) != previousMinute) // we have to change the time
{
  // quit the parking position
  if ((previousMinute >=29) && (previousMinute <= 32)) entry = 27; //check if the minute arm is close to 30 
  //entry = 27;
  gotoMinute (entry, 10); 
  delay(500);
  gotoMinute (entry, 0); 
  delay(200);

/* manage different cases :
 *  - minute = 0 : change the hour arm position
 *  - minute = 30 : change the hour arm to an intermediate position
 *  - minute pos = hour pos : escape maneuver
 *  - else just push the minute arm
 */

// - minute = 0 : change the hour arm position
// --------------------------------------------
  if (minute(madate) == 0) // we have to change the hour... 
  {
  circleFromToCW(30, 30+(5*(hour(madate)%12)), 0); // set the hours arm pushing clockwise
  delay(1000);
  
  // escape maneuver
  gotoMinute ((5*(hour(madate)%12))-2, -6);
  delay(500); 
  gotoMinute ((5*(hour(madate)%12))-2, -12);
  delay(500);
  gotoMinute ((5*(hour(madate)%12))+1, -12);
  delay(500);
  gotoMinute ((5*(hour(madate)%12)), 0);
  
  //set the minutes arm to the correct time (anticlockwise)
  circleFromToAW((5*(hour(madate)%12))-1,1+(120 + (5*(hour(madate)%12)) - minute(madate))%60, 0); 
  delay(1000);

  // set the arms to parking
  circleFromToCW(minute(madate)+1, (120 +30- minute(madate))%60, 0);  
  
  } // end minute = 0

  
// - minute = 30 : change the hour arm to an intermediate position <<<<<<<<<<< à vérifier
//   -------------------------------------------------------------
 else if (minute(madate) == 30) // we have to push the hour arm to put it between 2 graduations... 
  {
    circleFromToCW(30,(120+2+30+(5*(hour(madate)%12)))%60, 0); // set the hours arm pushing clockwise
    delay(1000);

    // escape maneuver
    gotoMinute (2+(5*(hour(madate)%12))-2, 0);
    delay(500);
    gotoMinute (2+(5*(hour(madate)%12))-2, -6);
     delay(500);
    gotoMinute (2+(5*(hour(madate)%12))-2, -12);
    delay(500);
    gotoMinute (2+(5*(hour(madate)%12))+1, -12);
    delay(500);
    gotoMinute (2+(5*(hour(madate)%12)), 0);
  
    circleFromToAW(2+(5*(hour(madate)%12))-1,(120 + 2+(5*(hour(madate)%12)) - minute(madate))%60, 0); //set the minutes arm to the correct time (anticlockwise)
    delay(1000);
  
    circleFromToCW(minute(madate)+1, (120 +30- minute(madate))%60, 0); // set the arms to parking 
  } // end minute = 30

// - minute pos = hour pos : escape maneuver
//   ---------------------------------------
else if (minute(madate) == 2+(5*(hour(madate)%12))) // we have to turn around the hour arm... 
  {
     circleFromToCW(30,(120-30+ 2+(5*(hour(madate)%12)))%60, 0); // set the minutes and hours arm pushing clockwise
    delay(1000);

    // escape maneuver
    gotoMinute (2+(5*(hour(madate)%12))-1, -12);
    delay(1000);
    gotoMinute (2+(5*(hour(madate)%12))+2, -12);
    delay(1000);
    gotoMinute (2+(5*(hour(madate)%12)), 0);
  
    circleFromToAW(2+(5*(hour(madate)%12))-1,59, 0); //rotate nearly one turn to set the minutes arm to the correct time (anticlockwise)
    delay(500);
  
    circleFromToCW(minute(madate)+1, (120 +30- minute(madate))%60, 0); // set the arms to parking 
  
  }

// - nothing special but pushing the minutes arm
//   ------------------------------------------- 
else { //nothing special but pushing the minutes arm
 
    circleFromToCW(30, (120+30+minute(madate))%60, 0); // set the minutes arm pushing clockwise
    delay(500);
    circleFromToAW(minute(madate)-1, -1+(120 + minute(madate)-30)%60, 0); // set the arms to parking 
  } // end otherwise

  // goto parking
  servo1Angle(-90);
  servo2Angle(-90);
  previousMinute = minute(madate);

} //end if we have to change the time

  delay (1000); // loop every second

#endif  // calibration or not
} //end loop

Credits

Mirko Pavleski

Mirko Pavleski

151 projects • 1288 followers

Comments