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