plotclock.c


1
// Plotclock
2
// cc - by Johannes Heberlein 2014
3
// v 1.02
4
// thingiverse.com/joo wiki.fablab-nuernberg.de
5
// units: mm; microseconds; radians
6
// origin: bottom left of drawing surface
7
// time library see http://playground.arduino.cc/Code/time
8
// RTC library see http://playground.arduino.cc/Code/time
9
// or http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
10
// Change log:
11
// 1.01 Release by joo at https://github.com/9a/plotclock
12
// 1.02 Additional features implemented by Dave:
13
// - added ability to calibrate servofaktor seperately for left and right servos
14
// - added code to support DS1307, DS1337 and DS3231 real time clock chips
15
// - see http://www.pjrc.com/teensy/td_libs_DS1307RTC.html for how to hook up the real time clock
16
17
// delete or mark the next line as comment if you don't need these
18
//#define CALIBRATION // enable calibration mode
19
//#define REALTIMECLOCK // enable real time clock
20
21
// When in calibration mode, adjust the following factor until the servos move exactly 90 degrees
22
#define SERVOFAKTORLEFT 900
23
#define SERVOFAKTORRIGHT 600
24
25
// Zero-position of left and right servo
26
// When in calibration mode, adjust the NULL-values so that the servo arms are at all times parallel
27
// either to the X or Y axis
28
#define SERVOLEFTNULL 2400
29
#define SERVORIGHTNULL 900
30
31
#define SERVOPINLIFT 2
32
#define SERVOPINLEFT 3
33
#define SERVOPINRIGHT 4
34
35
// lift positions of lifting servo
36
#define LIFT0 1080 // on drawing surface
37
#define LIFT1 925 // between numbers
38
#define LIFT2 725 // going towards sweeper
39
40
// speed of liftimg arm, higher is slower
41
#define LIFTSPEED 1500
42
43
// length of arms
44
#define L1 35
45
#define L2 55.1
46
#define L3 13.2
47
48
// origin points of left and right servo
49
#define O1X 22
50
#define O1Y -25
51
#define O2X 47
52
#define O2Y -25
53
54
#include <Time.h> // see http://playground.arduino.cc/Code/time
55
#include <Servo.h>
56
57
#ifdef REALTIMECLOCK
58
// for instructions on how to hook up a real time clock,
59
// see here -> http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
60
// DS1307RTC works with the DS1307, DS1337 and DS3231 real time clock chips.
61
// Please run the SetTime example to initialize the time on new RTC chips and begin running.
62
63
#include <Wire.h>
64
#include <DS1307RTC.h> // see http://playground.arduino.cc/Code/time
65
#endif
66
67
int servoLift = 1500;
68
69
Servo servo1; //
70
Servo servo2; //
71
Servo servo3; //
72
73
volatile double lastX = 75;
74
volatile double lastY = 47.5;
75
76
int last_min = 0;
77
78
void setup()
79
{
80
  #ifdef REALTIMECLOCK
81
  Serial.begin(9600);
82
  //while (!Serial) { ; } // wait for serial port to connect. Needed for Leonardo only
83
84
  // Set current time only the first to values, hh,mm are needed
85
  tmElements_t tm;
86
  if (RTC.read(tm))
87
  {
88
    setTime(tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year);
89
    Serial.println("DS1307 time is set OK.");
90
  }
91
  else
92
  {
93
    if (RTC.chipPresent())
94
    {
95
      Serial.println("DS1307 is stopped. Please run the SetTime example to initialize the time and begin running.");
96
    }
97
    else
98
    {
99
      Serial.println("DS1307 read error! Please check the circuitry.");
100
    }
101
    // Set current time only the first to values, hh,mm are needed
102
    setTime(19, 38, 0, 0, 0, 0);
103
  }
104
  #else
105
  // Set current time only the first to values, hh,mm are needed
106
  setTime(01, 00, 0, 0, 0, 0);
107
  #endif
108
109
  drawTo(22.5, 16.5);
110
  lift(0);
111
  servo1.attach(SERVOPINLIFT); // lifting servo
112
  servo2.attach(SERVOPINLEFT); // left servo
113
  servo3.attach(SERVOPINRIGHT); // right servo
114
  delay(1000);
115
116
}
117
118
void loop()
119
{
120
121
  #ifdef CALIBRATION
122
123
  
124
  // Servohorns will have 90° between movements, parallel to x and y axis
125
  drawTo(-3, 29.2);
126
  delay(500);
127
  drawTo(74.1, 28);
128
  delay(500);
129
  
130
  #else
131
132
133
  int i = 0;
134
  if (last_min != minute()) {
135
136
    if (!servo1.attached()) servo1.attach(SERVOPINLIFT);
137
    if (!servo2.attached()) servo2.attach(SERVOPINLEFT);
138
    if (!servo3.attached()) servo3.attach(SERVOPINRIGHT);
139
140
    lift(0);
141
142
    hour();
143
    while ((i + 1) * 10 <= hour())
144
    {
145
      i++;
146
    }
147
148
    number(3, 3, 111, 1);
149
    number(5, 25, i, 0.9);
150
    number(19, 25, (hour() - i * 10), 0.9);
151
    number(28, 25, 11, 0.9);
152
153
    i = 0;
154
    while ((i + 1) * 10 <= minute())
155
    {
156
      i++;
157
    }
158
    number(34, 25, i, 0.9);
159
    number(48, 25, (minute() - i * 10), 0.9);
160
    lift(2);
161
    drawTo(74.2, 47.5);
162
    lift(1);
163
    last_min = minute();
164
165
    servo1.detach();
166
    servo2.detach();
167
    servo3.detach();
168
  }
169
170
  #endif
171
172
}
173
174
// Writing numeral with bx by being the bottom left originpoint. Scale 1 equals a 20 mm high font.
175
// The structure follows this principle: move to first startpoint of the numeral, lift down, draw numeral, lift up
176
void number(float bx, float by, int num, float scale) {
177
178
  switch (num) {
179
180
    case 0:
181
    drawTo(bx + 12 * scale, by + 6 * scale);
182
    lift(0);
183
    bogenGZS(bx + 7 * scale, by + 10 * scale, 10 * scale, -0.8, 6.7, 0.5);
184
    lift(1);
185
    break;
186
    case 1:
187
188
    drawTo(bx + 3 * scale, by + 15 * scale);
189
    lift(0);
190
    drawTo(bx + 10 * scale, by + 20 * scale);
191
    drawTo(bx + 10 * scale, by + 0 * scale);
192
    lift(1);
193
    break;
194
    case 2:
195
    drawTo(bx + 2 * scale, by + 12 * scale);
196
    lift(0);
197
    bogenUZS(bx + 8 * scale, by + 14 * scale, 6 * scale, 3, -0.8, 1);
198
    drawTo(bx + 1 * scale, by + 0 * scale);
199
    drawTo(bx + 12 * scale, by + 0 * scale);
200
    lift(1);
201
    break;
202
    case 3:
203
    drawTo(bx + 2 * scale, by + 17 * scale);
204
    lift(0);
205
    bogenUZS(bx + 5 * scale, by + 15 * scale, 5 * scale, 3, -2, 1);
206
    bogenUZS(bx + 5 * scale, by + 5 * scale, 5 * scale, 1.57, -3, 1);
207
    lift(1);
208
    break;
209
    case 4:
210
    drawTo(bx + 10 * scale, by + 0 * scale);
211
    lift(0);
212
    drawTo(bx + 10 * scale, by + 20 * scale);
213
    drawTo(bx + 2 * scale, by + 6 * scale);
214
    drawTo(bx + 12 * scale, by + 6 * scale);
215
    lift(1);
216
    break;
217
    case 5:
218
    drawTo(bx + 2 * scale, by + 5 * scale);
219
    lift(0);
220
    bogenGZS(bx + 5 * scale, by + 6 * scale, 6 * scale, -2.5, 2, 1);
221
    drawTo(bx + 5 * scale, by + 20 * scale);
222
    drawTo(bx + 12 * scale, by + 20 * scale);
223
    lift(1);
224
    break;
225
    case 6:
226
    drawTo(bx + 2 * scale, by + 10 * scale);
227
    lift(0);
228
    bogenUZS(bx + 7 * scale, by + 6 * scale, 6 * scale, 2, -4.4, 1);
229
    drawTo(bx + 11 * scale, by + 20 * scale);
230
    lift(1);
231
    break;
232
    case 7:
233
    drawTo(bx + 2 * scale, by + 20 * scale);
234
    lift(0);
235
    drawTo(bx + 12 * scale, by + 20 * scale);
236
    drawTo(bx + 2 * scale, by + 0);
237
    lift(1);
238
    break;
239
    case 8:
240
    drawTo(bx + 5 * scale, by + 10 * scale);
241
    lift(0);
242
    bogenUZS(bx + 5 * scale, by + 15 * scale, 5 * scale, 4.7, -1.6, 1);
243
    bogenGZS(bx + 5 * scale, by + 5 * scale, 5 * scale, -4.7, 2, 1);
244
    lift(1);
245
    break;
246
247
    case 9:
248
    drawTo(bx + 9 * scale, by + 11 * scale);
249
    lift(0);
250
    bogenUZS(bx + 7 * scale, by + 15 * scale, 5 * scale, 4, -0.5, 1);
251
    drawTo(bx + 5 * scale, by + 0);
252
    lift(1);
253
    break;
254
255
    case 111:
256
257
    lift(0);
258
    
259
    drawTo(70, 46);
260
    drawTo(65, 43);
261
262
    drawTo(65, 49);
263
    drawTo(5, 49);
264
    drawTo(5, 45);
265
    drawTo(65, 45);
266
    drawTo(65, 40);
267
268
    drawTo(5, 40);
269
    drawTo(5, 35);
270
    drawTo(65, 35);
271
    drawTo(65, 30);
272
273
    drawTo(5, 30);
274
    drawTo(5, 25);
275
    drawTo(65, 25);
276
    drawTo(65, 20);
277
278
    drawTo(5, 20);
279
    drawTo(60, 44);
280
281
    drawTo(75.2, 47);
282
    lift(1);
283
284
    break;
285
286
    case 11:
287
    drawTo(bx + 5 * scale, by + 15 * scale);
288
    lift(0);
289
    bogenGZS(bx + 5 * scale, by + 15 * scale, 0.1 * scale, 1, -1, 1);
290
    lift(1);
291
    drawTo(bx + 5 * scale, by + 5 * scale);
292
    lift(0);
293
    bogenGZS(bx + 5 * scale, by + 5 * scale, 0.1 * scale, 1, -1, 1);
294
    lift(1);
295
    break;
296
297
  }
298
}
299
300
301
302
void lift(char lift) {
303
  switch (lift) {
304
    // room to optimize !
305
306
    case 0: //850
307
308
    if (servoLift >= LIFT0) {
309
      while (servoLift >= LIFT0)
310
      {
311
        servoLift--;
312
        servo1.writeMicroseconds(servoLift);
313
        delayMicroseconds(LIFTSPEED);
314
      }
315
    }
316
    else {
317
      while (servoLift <= LIFT0) {
318
        servoLift++;
319
        servo1.writeMicroseconds(servoLift);
320
        delayMicroseconds(LIFTSPEED);
321
322
      }
323
324
    }
325
326
    break;
327
328
    case 1: //150
329
330
    if (servoLift >= LIFT1) {
331
      while (servoLift >= LIFT1) {
332
        servoLift--;
333
        servo1.writeMicroseconds(servoLift);
334
        delayMicroseconds(LIFTSPEED);
335
336
      }
337
    }
338
    else {
339
      while (servoLift <= LIFT1) {
340
        servoLift++;
341
        servo1.writeMicroseconds(servoLift);
342
        delayMicroseconds(LIFTSPEED);
343
      }
344
345
    }
346
347
    break;
348
349
    case 2:
350
351
    if (servoLift >= LIFT2) {
352
      while (servoLift >= LIFT2) {
353
        servoLift--;
354
        servo1.writeMicroseconds(servoLift);
355
        delayMicroseconds(LIFTSPEED);
356
      }
357
    }
358
    else {
359
      while (servoLift <= LIFT2) {
360
        servoLift++;
361
        servo1.writeMicroseconds(servoLift);
362
        delayMicroseconds(LIFTSPEED);
363
      }
364
    }
365
    break;
366
  }
367
}
368
369
370
void bogenUZS(float bx, float by, float radius, int start, int ende, float sqee) {
371
  float inkr = -0.05;
372
  float count = 0;
373
374
  do {
375
    drawTo(sqee * radius * cos(start + count) + bx,
376
    radius * sin(start + count) + by);
377
    count += inkr;
378
  }
379
  while ((start + count) > ende);
380
381
}
382
383
void bogenGZS(float bx, float by, float radius, int start, int ende, float sqee) {
384
  float inkr = 0.05;
385
  float count = 0;
386
387
  do {
388
    drawTo(sqee * radius * cos(start + count) + bx,
389
    radius * sin(start + count) + by);
390
    count += inkr;
391
  }
392
  while ((start + count) <= ende);
393
}
394
395
396
void drawTo(double pX, double pY) {
397
  double dx, dy, c;
398
  int i;
399
400
  // dx dy of new point
401
  dx = pX - lastX;
402
  dy = pY - lastY;
403
  //path lenght in mm, times 4 equals 4 steps per mm
404
  c = floor(4 * sqrt(dx * dx + dy * dy));
405
406
  if (c < 1) c = 1;
407
408
  for (i = 0; i <= c; i++) {
409
    // draw line point by point
410
    set_XY(lastX + (i * dx / c), lastY + (i * dy / c));
411
412
  }
413
414
  lastX = pX;
415
  lastY = pY;
416
}
417
418
double return_angle(double a, double b, double c) {
419
  // cosine rule for angle between c and a
420
  return acos((a * a + c * c - b * b) / (2 * a * c));
421
}
422
423
void set_XY(double Tx, double Ty)
424
{
425
  delay(1);
426
  double dx, dy, c, a1, a2, Hx, Hy;
427
428
  // calculate triangle between pen, servoLeft and arm joint
429
  // cartesian dx/dy
430
  dx = Tx - O1X;
431
  dy = Ty - O1Y;
432
433
  // polar lemgth (c) and angle (a1)
434
  c = sqrt(dx * dx + dy * dy); //
435
  a1 = atan2(dy, dx); //
436
  a2 = return_angle(L1, L2, c);
437
438
  servo2.writeMicroseconds(floor(((a2 + a1 - M_PI) * SERVOFAKTORLEFT) + SERVOLEFTNULL));
439
440
  // calculate joinr arm point for triangle of the right servo arm
441
  a2 = return_angle(L2, L1, c);
442
  Hx = Tx + L3 * cos((a1 - a2 + 0.621) + M_PI); //36,5°
443
  Hy = Ty + L3 * sin((a1 - a2 + 0.621) + M_PI);
444
445
  // calculate triangle between pen joint, servoRight and arm joint
446
  dx = Hx - O2X;
447
  dy = Hy - O2Y;
448
449
  c = sqrt(dx * dx + dy * dy);
450
  a1 = atan2(dy, dx);
451
  a2 = return_angle(L1, (L2 - L3), c);
452
453
  servo3.writeMicroseconds(floor(((a1 - a2) * SERVOFAKTORRIGHT) + SERVORIGHTNULL));
454
455
}