Plotclock_v1_2.ino


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