Last active
May 23, 2017 00:30
-
-
Save chrisngobanh/7d4bf154f0f500ce80cb6a5d89314bd6 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
#include <Wire.h> | |
#include <DS1307RTC.h> | |
#include <Time.h> | |
#include <Wire.h> | |
#include <EEPROM.h> | |
#define A 13 | |
#define B 12 | |
#define C 11 | |
#define D 10 | |
#define DELAY 500 | |
const int hour_addr = 0; | |
const int minute_addr = 9; | |
const int second_addr = 17; | |
const int rotary_outputA = 6; | |
const int rotary_outputB = 7; | |
//Put all the pins in an array to make them easy to work with | |
const int stepper_pinIN[] { | |
2, //IN1 on the ULN2003 Board, BLUE end of the Blue/Yellow motor coil | |
3, //IN2 on the ULN2003 Board, PINK end of the Pink/Orange motor coil | |
4, //IN3 on the ULN2003 Board, YELLOW end of the Blue/Yellow motor coil | |
5 //IN4 on the ULN2003 Board, ORANGE end of the Pink/Orange motor coil | |
}; | |
//Define the full step sequence. | |
//With the pin (coil) states as an array of arrays | |
int fullStepCount = 4; | |
int fullSteps[][4] = { | |
{HIGH,HIGH,LOW,LOW}, | |
{LOW,HIGH,HIGH,LOW}, | |
{LOW,LOW,HIGH,HIGH}, | |
{HIGH,LOW,LOW,HIGH} | |
}; | |
int c[3][4][2] = { | |
{ {A, B}, {B, D}, {C, B}, {D, B} }, | |
{ {A, C}, {B, C}, {C, D}, {D, C} }, | |
{ {A, D}, {B, A}, {C, A}, {D, A} } | |
}; | |
int frames[][3] = | |
{ | |
{0b0001, 0b0000, 0b0000}, | |
{0b0000, 0b0001, 0b0000}, | |
{0b0000, 0b0000, 0b0001} | |
}; | |
//Keeps track of the current step. | |
//We'll use a zero based index. | |
int currentStep = 0; | |
//Keeps track of the current direction | |
//Relative to the face of the motor. | |
//Clockwise (true) or Counterclockwise(false) | |
//We'll default to clockwise | |
bool clockwise = true; | |
// How many steps to go before reversing, set to zero to not bounce. | |
//int targetSteps = 0; //targetSteps 0 means the motor will just run in a single direction. | |
int targetSteps = 2048; //2049 steps per rotation when wave or full stepping | |
//int targetSteps = 4096; //4096 steps per rotation when half stepping | |
int aState; | |
int aLastState; | |
// For rotating the hand | |
double queuedSteps = 0.0; | |
tmElements_t tm; | |
int cachedMinute; | |
int cachedHour; | |
// FOR DEBUGGING | |
bool getTime(const char *str) | |
{ | |
int Hour, Min, Sec; | |
if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false; | |
tm.Hour = Hour; | |
tm.Minute = Min; | |
tm.Second = Sec; | |
return true; | |
} | |
void rotateStepper(int min) { | |
queuedSteps += (min * (double)targetSteps / 60.0); | |
} | |
// Calculate how much to move the hands | |
void calculateRotation(int oldHour, int oldMinute, int newHour, int newMinute) { | |
int hourDifference = newHour - oldHour; | |
while (hourDifference > 6 || hourDifference < -6) { | |
if (hourDifference > 6) { | |
hourDifference -= 12; | |
} else { | |
hourDifference += 12; | |
} | |
} | |
int minuteDifference = newMinute - oldMinute; | |
while (minuteDifference > 30 || minuteDifference < -30) { | |
if (minuteDifference > 30) { | |
minuteDifference -= 60; | |
} else { | |
minuteDifference += 60; | |
} | |
} | |
// Rotate the stepper to the correct amount of minutes | |
rotateStepper(hourDifference * 60 + minuteDifference); | |
// DEBUG | |
/* | |
Serial.print("Hour: "); | |
Serial.print(hourDifference); | |
Serial.print(" | Minute: "); | |
Serial.print(minuteDifference); | |
Serial.println(); | |
Serial.print("Minutes to rotate: "); | |
Serial.println(hourDifference * 60 + minuteDifference); | |
*/ | |
} | |
void setup() { | |
while (!Serial); | |
// put your setup code here, to run once: | |
Serial.begin(9600); | |
// Configuring the pins for the Rotary Encoder | |
pinMode (rotary_outputA,INPUT); | |
pinMode (rotary_outputB,INPUT); | |
aLastState = digitalRead(rotary_outputA); | |
// Configuring the pins for the Stepper Motor | |
for (int i = 0; i < 4; i++) { | |
pinMode(stepper_pinIN[i], OUTPUT); | |
digitalWrite(stepper_pinIN[i], LOW); | |
} | |
// Configuring the pins for the LEDs | |
pinMode(A, INPUT); | |
pinMode(B, INPUT); | |
pinMode(C, INPUT); | |
pinMode(D, INPUT); | |
// DEBUG: Initializes the RTC module to compiler time. | |
/* | |
if (getTime(__TIME__)) { | |
RTC.write(tm); | |
} | |
*/ | |
// Initialize data from memory | |
int savedHour = EEPROM.read(hour_addr); | |
int savedMinute = EEPROM.read(minute_addr); | |
int savedSecond = EEPROM.read(second_addr); | |
// Save the last minute value | |
if (RTC.read(tm)) { | |
// This will initialize the clock hands from where it last left off | |
calculateRotation(savedHour, savedMinute, tm.Hour, tm.Minute); | |
EEPROM.write(hour_addr, tm.Hour); | |
EEPROM.write(minute_addr, tm.Minute); | |
EEPROM.write(second_addr, tm.Second); | |
cachedMinute = tm.Minute; | |
cachedHour = tm.Hour; | |
handleLEDLight(); | |
// DEBUG | |
// Serial.print(tm.Hour); | |
// Serial.print(":"); | |
// Serial.print(tm.Minute); | |
// tm.Hour = 1; | |
// RTC.write(tm); | |
// EEPROM.write(hour_addr, 1); | |
// EEPROM.write(minute_addr, 0); | |
} else { | |
if (RTC.chipPresent()) { | |
Serial.println("The DS1307 is stopped. Please run the SetTime"); | |
Serial.println("example to initialize the time and begin running."); | |
Serial.println(); | |
} else { | |
Serial.println("DS1307 read error! Please check the circuitry."); | |
Serial.println(); | |
} | |
} | |
} | |
void step(int steps[][4], int stepCount) { | |
// Increment the program field tracking the current step we are on | |
++currentStep; | |
//Then we can figure out what our current step within the sequence from the overall current step | |
//and the number of steps in the sequence | |
int currentStepInSequence = currentStep % stepCount; | |
//Figure out which step to use. If clock wise, it is the same is the current step | |
//if not clockwise, we fire them in the reverse order... | |
int directionStep = clockwise ? currentStepInSequence : (stepCount-1) - currentStepInSequence; | |
//Set the four pins to their proper state for the current step in the sequence, | |
//and for the current direction | |
for (int i = 0; i < 4; i++) { | |
digitalWrite(stepper_pinIN[i], steps[directionStep][i]); | |
} | |
// Hack to prevent overflow of currentStep | |
if (currentStep == 2048 || currentStep < 0) { | |
currentStep = 0; | |
} | |
// Serial.println(currentStep); | |
} | |
// For debugging | |
void print2digits(int number) { | |
if (number >= 0 && number < 10) { | |
Serial.write('0'); | |
} | |
Serial.print(number); | |
} | |
void handleLEDLight() { | |
int hr1 = cachedHour <= 12 ? cachedHour : cachedHour - 12; | |
int hr2 = tm.Hour <= 12 ? tm.Hour : tm.Hour - 12; | |
if (hr1 == 0) { | |
hr1 = 12; | |
} | |
if (hr2 == 0) { | |
hr2 = 12; | |
} | |
turnOffLED(hr1); | |
lightLED(hr2); | |
} | |
void loop() { | |
// Read the current value on the RTC | |
RTC.read(tm); | |
/** | |
* Rotary Encoder | |
*/ | |
aState = digitalRead(rotary_outputA); // Reads the "current" state of the outputA | |
// If the previous and the current state of the outputA are different, that means a Pulse has occured | |
if (aState != aLastState) { | |
// If the outputB state is different to the outputA state, that means the encoder is rotating clockwise | |
// One rotary encoder turn tick is 1 minute | |
if (digitalRead(rotary_outputB) != aState) { | |
tm.Minute++; | |
//cachedMinute++; | |
// Calculate the steps needed to rotate by 1 minute forward | |
// 2048 / 360 * 6 | |
rotateStepper(1); | |
//queuedSteps += ((double)targetSteps / 60.0); | |
} else { | |
tm.Minute--; | |
//cachedMinute--; | |
// Calculate the steps needed to rotate by 1 minute backwards | |
rotateStepper(-1); | |
//queuedSteps -= ((double)targetSteps / 60.0); | |
} | |
if (tm.Minute == 255) { | |
tm.Minute = 59; | |
tm.Hour -= 1; | |
} else if (tm.Minute == 60) { | |
// Minutes: Overflow from addition | |
tm.Minute = 0; | |
tm.Hour += 1; | |
} | |
// Hours: Overflow from subtraction | |
if (tm.Hour == 255) { | |
tm.Hour = 23; | |
} else if (tm.Hour == 24) { | |
// Minutes: Overflow from addition | |
tm.Hour = 0; | |
} | |
RTC.write(tm); | |
EEPROM.write(hour_addr, tm.Hour); | |
EEPROM.write(minute_addr, tm.Minute); | |
EEPROM.write(second_addr, tm.Second); | |
cachedMinute = tm.Minute; | |
// DEBUG: | |
Serial.print("Ok, Time = "); | |
print2digits(tm.Hour); | |
Serial.write(':'); | |
print2digits(tm.Minute); | |
Serial.write(':'); | |
print2digits(tm.Second); | |
Serial.println(); | |
} | |
// This handles rotating the stepper motor by 1 minute | |
if (cachedMinute != tm.Minute) { | |
// DEBUG | |
// Serial.println("Rotate by 1 minute."); | |
/* | |
Serial.print("old minute: "); | |
Serial.print(cachedMinute); | |
Serial.print(" || new minute: "); | |
Serial.println(tm.Minute); | |
*/ | |
// Update cachedMinute Value | |
cachedMinute = tm.Minute; | |
// Move stepper motor by 1 minute | |
rotateStepper(1); | |
// queuedSteps += ((double)targetSteps / 60.0); | |
EEPROM.write(hour_addr, tm.Hour); | |
EEPROM.write(minute_addr, tm.Minute); | |
EEPROM.write(second_addr, tm.Second); | |
/* | |
Serial.println(tm.Hour); | |
Serial.println(tm.Minute); | |
Serial.println(tm.Second); | |
*/ | |
} | |
// Rotary Encoder | |
aLastState = aState; // Updates the previous state of the rotary_outputA with the current state | |
// NOTE: DONT MOVE THIS CODE PLZZ | |
if (queuedSteps >= 1.0) { | |
// Hack to prevent overflowing of currentStep | |
if (!clockwise) { | |
currentStep = 0; | |
} | |
clockwise = true; | |
step(fullSteps,fullStepCount); | |
queuedSteps -= 1.0; | |
//Serial.print("Steps Left: "); | |
//Serial.println(queuedSteps); | |
} else if (queuedSteps <= -1.0) { | |
// Hack to prevent overflowing of currentStep | |
if (clockwise) { | |
currentStep = 0; | |
} | |
clockwise = false; | |
step(fullSteps,fullStepCount); | |
queuedSteps += 1.0; | |
} | |
if (cachedHour != tm.Hour) { | |
handleLEDLight(); | |
/* | |
Serial.print("old hour: "); | |
Serial.print(cachedHour); | |
Serial.print(" || new hour: "); | |
Serial.println(tm.Hour); | |
*/ | |
cachedHour = tm.Hour; | |
} | |
/* | |
* 2 milliseconds seems to be about the shortest delay that is usable. | |
* Anything lower and the motor starts to freeze. | |
*/ | |
delay(2); | |
// LED Debug | |
// changeHour(4); | |
//delay(3000); | |
} | |
// Lights an LED using the LED number as an input | |
void lightLED(int p) { | |
switch (p) { | |
case 1: light(c[2][0]); break; | |
case 2: light(c[1][0]); break; | |
case 3: light(c[0][0]); break; | |
case 4: light(c[0][1]); break; | |
case 5: light(c[1][1]); break; | |
case 6: light(c[2][1]); break; | |
case 7: light(c[1][2]); break; | |
case 8: light(c[2][2]); break; | |
case 9: light(c[0][2]); break; | |
case 10: light(c[2][3]); break; | |
case 11: light(c[1][3]); break; | |
case 12: light(c[0][3]); break; | |
} | |
} | |
// Turn off LED using the LED number as input | |
void turnOffLED(int p) { | |
switch (p) { | |
case 1: turnOff(c[2][0]); break; | |
case 2: turnOff(c[1][0]); break; | |
case 3: turnOff(c[0][0]); break; | |
case 4: turnOff(c[0][1]); break; | |
case 5: turnOff(c[1][1]); break; | |
case 6: turnOff(c[2][1]); break; | |
case 7: turnOff(c[1][2]); break; | |
case 8: turnOff(c[2][2]); break; | |
case 9: turnOff(c[0][2]); break; | |
case 10: turnOff(c[2][3]); break; | |
case 11: turnOff(c[1][3]); break; | |
case 12: turnOff(c[0][3]); break; | |
} | |
} | |
// Lights all the LEDs from 1-n | |
void lightLEDs(int n) { | |
for (int j = 1; j <= n; j++) { | |
lightLED(j); | |
turnOffLED(j); | |
} | |
} | |
void turnOffAll(int n) { | |
for (int i = 1; i <= n; i++) { | |
turnOffLED(i); | |
} | |
} | |
/* LED lighting functions */ | |
// Turns off an LED, takes an array of two pins as input | |
void turnOff( int pins[2] ) { | |
pinMode(pins[0], INPUT); | |
pinMode(pins[1], INPUT); | |
} | |
// Lights an LED, takes an array of two pins as input | |
void light( int pins[2] ) { | |
pinMode( pins[0], OUTPUT ); | |
digitalWrite( pins[0], HIGH ); | |
pinMode( pins[1], OUTPUT ); | |
digitalWrite( pins[1], LOW ); | |
} | |
void changeHour(int i) { | |
for (int j = 1; j <= 12; j++) { | |
int num = (j+i < 13 ? j+i : j+i-12); | |
test(num, DELAY); | |
} | |
} | |
/* Testing functions */ | |
void test( int pin, int speed ) { | |
setup(); | |
lightLED(pin); | |
delay(speed); | |
} | |
void test_loop() { | |
int speed = 500; | |
for (int i = 1; i <= 12; i++) | |
test(i, speed); | |
} | |
void display( int frame[3], int duration ) { | |
int times = 0; | |
int x = 0; | |
int y = 0; | |
while( times < duration ) { | |
for( y = 0; y < 3; y++ ) { | |
for( x = 0; x < 4; x++ ) { | |
setup(); | |
if (frame[y] & (0b100 >> x)) { | |
light(c[y][x]); | |
delayMicroseconds(DELAY); | |
times++; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment