Last active
July 11, 2019 19:25
-
-
Save goebish/2cfe853ec5b588c7b889b85a3a4cd441 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define CALIBRATE | |
//#define GRID | |
const double SERVO_LEFT_ZERO = 1890; // 1800 | |
const double SERVO_RIGHT_ZERO = 900; // 850 | |
const double SERVO_LEFT_SCALE = 600; // 600 | |
const double SERVO_RIGHT_SCALE = 540; // 570 | |
//#define OPTION_12_HOUR //12 or 24 hour time | |
//#define OPTION_MONTH_DAY //day/month or month/day | |
const double DRAW_DELAY = 5; // 3 | |
/////////////////////////////////////////////////////////////////////////////// | |
// Plotclock | |
// cc - by Johannes Heberlein 2014 | |
// modified for glow clock - Tucker Shannon 2018 | |
// improved - 12Me21 2018 | |
// v 1.06 | |
// 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. | |
/////////////////////////////////////////////////////////////////////////////// | |
// modified for DS3231 RTC | |
// long press on button display date | |
#include <Servo.h> //servo controller | |
#include <Wire.h> //probably an IO library | |
#include <DS3231.h> //RTC controller | |
//pins | |
const int SERVO_LEFT_PIN = 6; | |
const int SERVO_RIGHT_PIN = 5; | |
const int LED_PIN = 13; | |
const int BUTTON_PIN = 7; | |
//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; | |
const char weekDays[] = {4,5,11, 8,9,1, 10,1,12, 8,3,9, 2,6,13, 7,0,8, 7,9,11}; //character set: AEFHMORSTUWNDI | |
double lastX = HOME_X, lastY = HOME_Y; | |
bool lightOn = false; | |
DS3231 RTC; | |
void setup() { | |
pinMode(LED_PIN, OUTPUT); | |
digitalWrite(LED_PIN, LOW); | |
pinMode(BUTTON_PIN, INPUT_PULLUP); | |
RTC.begin(); | |
} | |
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 | |
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 | |
#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 | |
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); | |
RTCDateTime tm; | |
tm = RTC.getDateTime(); | |
//this part is messy sorry | |
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); | |
// / | |
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 | |
drawDigit(5,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT,13+weekDays[(tm.dayOfWeek-1)*3]); | |
drawDigit(5+DAY_WIDTH+5,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT,13+weekDays[(tm.dayOfWeek-1)*3+1]); | |
drawDigit(5+(DAY_WIDTH+5)*2,DAY_BOTTOM,DAY_WIDTH,DAY_HEIGHT,13+weekDays[(tm.dayOfWeek-1)*3+2]); | |
}else | |
// time | |
{ | |
#ifdef OPTION_12_HOUR | |
if(tm.Hour >= 12){ | |
tm.Hour = tm.Hour - 12; | |
drawDigit(5,35,1,1,10); | |
} | |
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); | |
//: | |
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 //GRID | |
drawTo(HOME_X, HOME_Y); | |
#endif //CALIBRATE | |
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); | |
#ifdef OPTION_12_HOUR | |
break;case 10: //dot | |
digitStart(0,0); | |
//digitMove(0,1); | |
//digitMove(1,1); | |
//digitMove(1,0); | |
#endif | |
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); | |
//letters for the day of the week | |
break;case 13: //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 14: //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 15: //F | |
digitStart(0,0); | |
digitMove(0,1); | |
digitMove(1,1); | |
light(LOW); | |
digitStart(0,1/2); | |
digitMove(1,1/2); | |
break;case 16: //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 17: //M | |
digitStart(0,0); | |
digitMove(0,1); | |
digitMove(1/2,1/2); | |
digitMove(1,1); | |
digitMove(1,0); | |
break;case 18: //O (0) | |
digitStart(1,1/2); | |
digitArc(1/2,1/2, 1/2,1/2, 0, 1.02); | |
break;case 19: //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 20: //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 21: //T | |
digitStart(1,1); | |
digitMove(-1/2,1); //bad | |
light(LOW); | |
digitStart(1/2,1); | |
digitMove(1/2,0); | |
break;case 22: //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 23: //W | |
digitStart(0,1); | |
digitMove(0,0); | |
digitMove(1/2,1/2); | |
digitMove(1,0); | |
digitMove(1,1); | |
break;case 24: //N | |
digitStart(0,0); | |
digitMove(0,1); | |
digitMove(1,0); | |
digitMove(1,1); | |
break;case 25: //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 26: //I | |
digitStart(1/2,1); | |
digitMove(1/2,0); | |
} | |
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)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment