项目代码
- //#define CALIBRATE
- //#define GRID // After Calibration this mode should plot a pretty good dot grid on the screen
-
- // See additional calibration comments in the code down by the "#ifdef CALIBRATE"
- // Adjust these values for servo arms in position for state 1 _|
- const double SERVO_LEFT_ZERO = 1600;
- const double SERVO_RIGHT_SCALE = 690; // + makes rotate further left
-
- // Adjust these values for servo arms in position for state 2 |_
- const double SERVO_RIGHT_ZERO = 650;
- const double SERVO_LEFT_SCALE = 650;
-
- #define OPTION_12_HOUR // 12 or comment out this line for 24 hour time
- //#define OPTION_MONTH_DAY // commented out = month/day or uncomment the line for day/month
-
- const double DRAW_DELAY = 5; // 3
-
- ///////////////////////////////////////////////////////////////////////////////
-
- // Plotclock
- // cc - by Johannes Heberlein 2014
- // modified for glow clock - Tucker Shannon 2018
- // improved - 12Me21 2018
-
- // v 1.07ddd
- // thingiverse.com/joo wiki.fablab-nuernberg.de
- // thingiverse.com/TuckerPi
- // units: mm; microseconds; radians
- // origin: bottom left of drawing surface
- // time library see http://playground.arduino.cc/Code/time
- // RTC library see http://playground.arduino.cc/Code/time
- // or http://www.pjrc.com/teensy/td_libs_DS1307RTC.html
- // Change log:
- // 1.01 Release by joo at https://github.com/9a/plotclock
- // 1.02 Additional features implemented by Dave (https://github.com/Dave1001/):
- // - added ability to calibrate servofaktor seperately for left and right servos
- // - added code to support DS1307, DS1337 and DS3231 real time clock chips
- // - see http://www.pjrc.com/teensy/td_libs_DS1307RTC.html for how to hook up the real time clock
- // 1.03 Fixed the length bug at the servoplotclockogp2 angle calculation, other fixups
- // 1.04 Modified for Tuck's glow clock
- // 1.05 Modified calibration mode to draw a 4 point square instead
- // 1.06 Rewrote most of the code, improved calibration, added date drawing, fixed bug in angle calculations, etc.
- // 1.07ddd Reverted code to return it to using the DS1307 Library and removed the long press date code.
- // Did this because I liked this codes calibration code better than the v1.05 calibration code that drew a square.
- // Plus the bug fixes in 1.06 are good
- // Added comments on how to calibrate
- // Split Number drawing and letter drawing into two different functions
- // Improved the letter "I"
-
- ///////////////////////////////////////////////////////////////////////////////
-
- #include <Time.h> // see http://playground.arduino.cc/Code/time
- #include <TimeLib.h>
- #include <Servo.h> //servo controller
- #include <Wire.h>
- #include <DS1307RTC.h> // see http://playground.arduino.cc/Code/time
-
- //pins
- const int SERVO_LEFT_PIN = 6;
- const int SERVO_RIGHT_PIN = 5;
- const int LED_PIN = 12;
- const int BUTTON_PIN = 13;
-
- //Sizes
- const double LOWER_ARM = 35; //servo to lower arm joint
- const double UPPER_ARM_LEFT = 56; //lower arm joint to led
- const double LED_ARM = 13.5; //upper arm joint to led
- const double UPPER_ARM = 45; //lower arm joint to upper arm joint
- double cosineRule(double a, double b, double c);
- const double LED_ANGLE = cosineRule(UPPER_ARM_LEFT,UPPER_ARM,LED_ARM);
-
- //Location of servos relative to origin
- const double SERVO_LEFT_X = 22;
- const double SERVO_LEFT_Y = -32;
- const double SERVO_RIGHT_X = SERVO_LEFT_X + 25.5;
- const double SERVO_RIGHT_Y = SERVO_LEFT_Y;
-
- // lovely macros
- #define radian(angle) (M_PI*2* angle)
- #define dist(x,y) sqrt(sq(x)+sq(y))
- #define angle(x,y) atan2(y,x)
-
- //digit location/size constants
- const double TIME_BOTTOM = 12;
- const double TIME_WIDTH = 11;
- const double TIME_HEIGHT = 18; //16;
-
- const double DAY_WIDTH = 7;
- const double DAY_HEIGHT = 12;
- const double DAY_BOTTOM = 5;
- const double DATE_BOTTOM = 24;
-
- const double HOME_X = 55, HOME_Y = -5;
- Servo servoLeft, servoRight;
-
- // Sunday is the first triple
- const char weekDays[] = {8,10,12, 5,6,12, 9,10,2, 11,2,13, 9,4,10, 3,7,14, 8,1,9}; //character set: AEFHMORSTUWNDI
-
- double lastX = HOME_X, lastY = HOME_Y;
-
- bool lightOn = false;
-
- void setup() {
- pinMode(LED_PIN, OUTPUT);
- digitalWrite(LED_PIN, LOW);
- pinMode(BUTTON_PIN, INPUT_PULLUP);
- }
-
- void light(bool state){
- lightOn = state == HIGH; //I'm *pretty* sure HIGH/LOW are just true/false, but...
- delay(100);
- digitalWrite(LED_PIN, state);
- }
-
- const int LONG_PRESS_DURATION = 750;
-
- void loop(){
- if(digitalRead(BUTTON_PIN) != LOW)
- return;
-
- if (!servoLeft.attached()) servoLeft.attach(SERVO_LEFT_PIN);
- if (!servoRight.attached()) servoRight.attach(SERVO_RIGHT_PIN);
- #ifdef CALIBRATE
- // Pressing the button alternates the servo arms between 2 states.
- // State one if left arm pointing to 9 o'clock and right arm pointing to 12 o'clock _|
- // State two if left arm pointing to 12 o'clock and right arm pointing to 3 o'clock |_
- // At the very top of the code you adjust the 4 constants to get the arms into these exact positions.
- // Adjust SERVO_LEFT_ZERO so that the left servo points to 9 o'clock when in state one
- // Adjust SERVO_RIGHT_SCALE so that the right servo points to 12 o'clock when in state one
-
- // Adjust SERVO_RIGHT_ZERO so that the right servo points to 3 o'clock when in state two
- // Adjust SERVO_LEFT_SCALE so that the left servo points to 12 o'clock when in state two
-
- static bool half;
- servoLeft.writeMicroseconds(floor(SERVO_LEFT_ZERO + (half ? - M_PI/2 : 0) * SERVO_LEFT_SCALE ));
- servoRight.writeMicroseconds(floor(SERVO_RIGHT_ZERO + (half ? 0 : M_PI/2 ) * SERVO_RIGHT_SCALE ));
- light(half ? LOW : HIGH);
- half = !half;
- delay(2000);
- #else //CALIBRATE
- #ifdef GRID
- for(int i = 0; i <= 70; i += 10)
- for(int j = 0; j <= 40; j += 10){
- drawTo(i, j);
- light(HIGH);
- light(LOW);
- }
- #else //GRID
- delay(10); // debounce
- uint32_t longpress = millis() + LONG_PRESS_DURATION;
- while ((!digitalRead(BUTTON_PIN)) && (millis() < longpress))
- { }; // wait
- bool date = false;
- if (millis() >= longpress)
- date = true;
-
- drawTo(HOME_X, 0);
- tmElements_t tm;
- //time_t tt;
- uint8_t dayOfWeek; // 0 = Sunday, 6 = Saturday
- if (RTC.read(tm))
- {
- setTime(tm.Hour,tm.Minute,tm.Second,tm.Day,tm.Month,tm.Year);
- //tt = makeTime(tm); // need a normal time so it can be converted to day of week. Sunday = 1
- dayOfWeek = dayOfWeek(makeTime(tm)) - 1; // the minus 1 normalized so Sunday = 0
- }
- if (date) {
- #ifdef OPTION_MONTH_DAY
- int temp = tm.Day;
- tm.Day = tm.Month;
- tm.Month = temp;
- #endif
- //draw month
- if(tm.Month / 10)
- drawDigit(70-(DAY_WIDTH+3)*5, DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, tm.Month / 10);
- drawDigit(70-(DAY_WIDTH+3)*4, DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, tm.Month % 10);
- // Draw Slash
- drawDigit(70-(DAY_WIDTH+3)*3, DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, 12);
- //day
- if(tm.Day / 10){
- drawDigit(70-(DAY_WIDTH+3)*2, DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, tm.Day / 10);
- drawDigit(70-(DAY_WIDTH+3), DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, tm.Day % 10);
- }else
- drawDigit(70-(DAY_WIDTH+3)*2, DATE_BOTTOM, DAY_WIDTH, DAY_HEIGHT, tm.Day % 10);
- //weekday
- drawChar(5,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT,weekDays[dayOfWeek*3]);
- drawChar(5+DAY_WIDTH+5,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT, weekDays[dayOfWeek*3+1]);
- drawChar(5+(DAY_WIDTH+5)*2,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT,weekDays[dayOfWeek*3+2]);
-
- } else {
- #ifdef OPTION_12_HOUR
- if(tm.Hour >= 12){
- tm.Hour = tm.Hour - 12;
- // drawDigit(5,35,1,1,10); // Draws a DOT - Not sure why
- }
- if(tm.Hour == 0)
- tm.Hour = 12;
- #endif
- //draw hour
- if(tm.Hour / 10)
- drawDigit(3, TIME_BOTTOM, TIME_WIDTH, TIME_HEIGHT, tm.Hour / 10);
- drawDigit(3+TIME_WIDTH+3, TIME_BOTTOM, TIME_WIDTH, TIME_HEIGHT, tm.Hour % 10);
- // Draw colon
- drawDigit((69-TIME_WIDTH)/2, TIME_BOTTOM, TIME_WIDTH, TIME_HEIGHT, 11);
- //minute
- drawDigit(69-(TIME_WIDTH+3)*2, TIME_BOTTOM, TIME_WIDTH, TIME_HEIGHT, tm.Minute / 10);
- drawDigit(72-(TIME_WIDTH+3), TIME_BOTTOM, TIME_WIDTH, TIME_HEIGHT, tm.Minute % 10);
-
- }
- #endif // NOT CALIBRATE OR GRID
- drawTo(HOME_X, HOME_Y);
- #endif // GRID or Normal Plot Time
- servoLeft.detach();
- servoRight.detach();
- }
-
- #define digitMove(dx, dy) drawTo(x + width*dx, y + height*dy)
- #define digitStart(dx, dy) digitMove(dx, dy); light(HIGH)
- #define digitArc(dx, dy, rx, ry, start, last) drawArc(x + width*dx, y + height*dy, width*rx, height*ry, radian(start), radian(last))
-
- // Symbol is drawn with the lower left corner at (x,y) and a size of (width,height).
- void drawDigit(double x, double y, double width, double height, char digit) {
- //see macros for reference
- switch (digit) {
- case 0: //
- digitStart(1/2,1);
- digitArc(1/2,1/2, 1/2,1/2, 1/4, -3/4);
- //digitStart(1,1/2);
- //digitArc(1/2,1/2, 1/2,1/2, 0, 1.02);
- break;
- case 1: //
- digitStart(1/4,7/8);
- digitMove(1/2,1);
- digitMove(1/2,0);
- break;
- case 2: //
- digitStart(0,3/4);
- digitArc(1/2,3/4, 1/2,1/4, 1/2, -1/8);
- digitArc(1,0, 1,1/2, 3/8, 1/2);
- digitMove(1,0);
- break;
- case 3:
- digitStart(0,3/4);
- digitArc(1/2,3/4, 1/2,1/4, 3/8, -1/4);
- digitArc(1/2,1/4, 1/2,1/4, 1/4, -3/8);
- break;
- case 4:
- digitStart(1,3/8);
- digitMove(0,3/8);
- digitMove(3/4,1);
- digitMove(3/4,0);
- break;
- case 5: //wayy too many damn lines
- digitStart(1,1);
- digitMove(0,1);
- digitMove(0,1/2);
- digitMove(1/2,1/2);
- digitArc(1/2,1/4, 1/2,1/4, 1/4, -1/4);
- digitMove(0,0);
- break;
- case 6:
- digitStart(0,1/4);
- digitArc(1/2,1/4, 1/2,1/4, 1/2, -1/2);
- digitArc(1,1/2, 1,1/2, 1/2, 1/4);
- break;
- case 7:
- digitStart(0,1);
- digitMove(1,1);
- digitMove(1/4,0);
- break;
- case 8:
- digitStart(1/2,1/2);
- digitArc(1/2,3/4, 1/2,1/4, -1/4, 3/4);
- digitArc(1/2,1/4, 1/2,1/4, 1/4, -3/4);
- break;
- case 9:
- digitStart(1,3/4);
- digitArc(1/2,3/4, 1/2,1/4, 0, 1);
- digitMove(3/4,0);
- break;
- case 10: //dot
- digitStart(0,0);
- //digitMove(0,1);
- //digitMove(1,1);
- //digitMove(1,0);
- break;
- case 11: //colon
- digitStart(1/2,3/4);
- light(LOW);
- digitStart(1/2,1/4);
- break;
- case 12: //slash
- digitStart(3/4,5/4);
- digitMove(1/4,-1/4);
- break;
- }
- light(LOW);
- }
-
- void drawChar(double x, double y, double width, double height, char digit) {
- //see macros for reference
- switch (digit) {
- //letters for the day of the week
- case 1: //A
- digitStart(0,0);
- digitMove(1/2,1);
- digitMove(1,0);
- light(LOW);
- digitStart(1/4,1/2);
- digitMove(3/4,1/2);
- break;
- case 2: //E
- digitStart(1,0);
- digitMove(0,0);
- digitMove(0,1);
- digitMove(1,1);
- light(LOW);
- digitStart(0,1/2);
- digitMove(1,1/2);
- break;
- case 3: //F
- digitStart(0,0);
- digitMove(0,1);
- digitMove(1,1);
- light(LOW);
- digitStart(0,1/2);
- digitMove(1,1/2);
- break;
- case 4: //H
- digitStart(0,1);
- digitMove(0,0);
- light(LOW);
- digitStart(0,1/2);
- digitMove(1,1/2);
- light(LOW);
- digitStart(1,1);
- digitMove(1,0);
- break;
- case 5: //M
- digitStart(0,0);
- digitMove(0,1);
- digitMove(1/2,1/2);
- digitMove(1,1);
- digitMove(1,0);
- break;
- case 6: //O (0)
- digitStart(1,1/2);
- digitArc(1/2,1/2, 1/2,1/2, 0, 1.02);
- break;
- case 7: //R
- digitStart(0,0);
- digitMove(0,1);
- digitMove(1/2,1);
- digitArc(1/2,3/4, 1/2,1/4, 1/4, -1/4);
- digitMove(0,1/2);
- digitMove(1,0);
- break;
- case 8: //S
- digitStart(0,0);
- digitMove(1/2,0);
- digitArc(1/2,1/4, 1/2,1/4, -1/4, 1/4);
- digitArc(1/2,3/4, 1/2,1/4, 3/4, 1/4);
- digitMove(1,1);
- break;
- case 9: //T
- digitStart(1,1);
- digitMove(-1/2,1); //bad
- light(LOW);
- digitStart(1/2,1);
- digitMove(1/2,0);
- break;
- case 10: //U
- digitStart(0,1);
- digitMove(0,1/4);
- digitArc(1/2,1/4, 1/2,1/4, -1/2, 0);
- digitMove(1,1);
- break;
- case 11: //W
- digitStart(0,1);
- digitMove(0,0);
- digitMove(1/2,1/2);
- digitMove(1,0);
- digitMove(1,1);
- break;
- case 12: //N
- digitStart(0,0);
- digitMove(0,1);
- digitMove(1,0);
- digitMove(1,1);
- break;
- case 13: //D
- digitStart(0,0);
- digitMove(0,1);
- digitMove(1/2,1);
- digitArc(1/2,1/2, 1/2,1/2, 1/4,-1/4);
- digitMove(0,0);
- break;
- case 14: //I
- digitStart(1/2,1);
- digitMove(1/2,0);
- light(LOW);
- digitStart(0,0);
- digitMove(1,0);
- light(LOW);
- digitStart(1,1);
- digitMove(0,1);
- break;
- }
- light(LOW);
- }
-
- #define ARCSTEP 0.05 //0.05 //should change depending on radius...
- void drawArc(double x, double y, double rx, double ry, double pos, double last) {
- if(pos < last)
- for(; pos <= last; pos += ARCSTEP)
- drawTo(x + cos(pos)*rx, y + sin(pos)*ry);
- else
- for(; pos >= last; pos -= ARCSTEP)
- drawTo(x + cos(pos)*rx, y + sin(pos)*ry);
- }
-
- //didn't really change this
- void drawTo(double pX, double pY) {
- double dx, dy, c;
- int i;
-
- // dx dy of new point
- dx = pX - lastX;
- dy = pY - lastY;
- //path length in mm, times 4 equals 4 steps per mm
- c = floor(4 * dist(dx,dy));
-
- if (c < 1)
- c = 1;
-
- // draw line point by point
- for (i = 1; i <= c; i++){
- set_XY(lastX + (i * dx / c), lastY + (i * dy / c));
- if (lightOn)
- delay(DRAW_DELAY);
- }
-
- lastX = pX;
- lastY = pY;
- }
-
- // cosine rule for angle between c and a
- double cosineRule(double a, double b, double c) {
- return acos((sq(a)+sq(c)-sq(b))/(2*a*c));
- }
-
- void set_XY(double x, double y) {
- //Calculate triangle between left servo, left arm joint, and light
- //Position of pen relative to left servo
- //rectangular
- double penX = x - SERVO_LEFT_X;
- double penY = y - SERVO_LEFT_Y;
- //polar
- double penAngle = angle(penX,penY);
- double penDist = dist(penX,penY);
- //get angle between lower arm and a line connecting the left servo and the pen
- double bottomAngle = cosineRule(LOWER_ARM, UPPER_ARM_LEFT, penDist);
-
- servoLeft.writeMicroseconds(floor(SERVO_LEFT_ZERO + (bottomAngle + penAngle - M_PI) * SERVO_LEFT_SCALE));
-
- //calculate middle arm joint location
- double topAngle = cosineRule(UPPER_ARM_LEFT, LOWER_ARM, penDist);
- double lightAngle = penAngle - topAngle + LED_ANGLE + M_PI;
- double jointX = x - SERVO_RIGHT_X + cos(lightAngle) * LED_ARM;
- double jointY = y - SERVO_RIGHT_Y + sin(lightAngle) * LED_ARM;
-
- bottomAngle = cosineRule(LOWER_ARM, UPPER_ARM, dist(jointX, jointY));
- double jointAngle = angle(jointX, jointY);
-
- servoRight.writeMicroseconds(floor(SERVO_RIGHT_ZERO + (jointAngle - bottomAngle) * SERVO_RIGHT_SCALE));
- }
复制代码
|