Skip to content

Instantly share code, notes, and snippets.

@chrisngobanh

chrisngobanh/tt-clock.ino

Last active May 23, 2017
Embed
What would you like to do?
#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