Hardware components | ||||||
| × | 1 | ||||
| × | 2 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
|
This is a clock that is abstracted and designed from Aeropic (Nick on Thingiverse), which I made with minor changes to the hardware and the code. Motion is obtained from two RC servos whose arms are connected in a W shape. At the middle of the W sits a screw able to push the minutes arm via a pad. The minutes arm itself can push the hours arm. In the original project are used 2x "GWS pico servo" motors, while I use a cheap SG 90 servos. As these servo motors turn in the opposite direction, I made a change, so that the face of the clock is turned for 180 degrees.
Servos are driven by NodeMCU 1.0 (ESP12E) module. The clock being connected to the internet and it can get the time from an NTP server. I have replaced NTP servers in the original code, because the existing ones did not respond. The clock is then able to automatically set the arms at the right time, move the arms every minute and perfom the adequate maneuvers to keep the arms in the right position. It is then really fun to see it doing its mesmerizing job.
Basically there is nothing to modify in the firmware except few parameters to trim.
The dedicated line are all marked with a "<<<<<<<<<<<<<<<<<<<" pattern.
Before inserting the clock arm axis you should calibrate the servos.
Uncomment the #define CALIBRATION line, complie and upload the binary.The servo arms will move every 5 sec from fully horizontal to fully vertical (down pointing).
Adapt the servo left and right null values to get the arms horizontal.
/* =============================================================
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
Comments