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 | }
|