MetaView
Published © MIT

Uferclock

Bring an old tower clock to life with an Arduino Uno board.

IntermediateShowcase (no instructions)5 hours3,267
Uferclock

Things used in this project

Story

Read more

Schematics

H-Bridge

Code

Arduino Code

Arduino
This code controls the tower clock. There is a basic serial interface to adjust the internal date and time and to set the positions of the clock's handles.
During development there was a 2 line alphanumeric LCD display with keypad connected. Some code lines are still there from this time.
#include <DCF77.h>
#include <EEPROM.h>
#include <Wire.h>
#include <RTClib.h>
//#include <LiquidCrystal.h>
//#include <LCDKeypad.h>
#include <string.h>
#include <stdlib.h>

//LCDKeypad lcd;
RTC_DS1307 rtc;
DCF77 dcf(2, INT0);

int twrMinutes = 0;
int twrStep = 0;
int twrReset = 0;
int twrLightSwitch = 0;
int twrLightValue = 100;

void handleTowerClock(DateTime now);
int handleRTC(DateTime now);
void HandleBluetooth();
void doDebugOutput(char line, int row, int col);
boolean isSommerTime(int hour, int day, int month, int year, int dow);

byte c_up[8] = {
	B00100,
	B01110,
	B10101,
	B00100,
	B00100,
	B00100,
	B00100,
	B00100,
};

byte c_down[8] = {
	B00100,
	B00100,
	B00100,
	B00100,
	B00100,
	B10101,
	B01110,
	B00100,
};

byte c_select[8] = {
	B00000,
	B01110,
	B11111,
	B11111,
	B11111,
	B11111,
	B01110,
	B00000,
};

#define CLOCK_DISPLAY	0
#define CLOCK_MENU		1
#define CLOCK_TOWER		2
#define CLOCK_RESET		3
#define CLOCK_ADJUST	4
#define CLOCK_DEBUG		5
#define MAX_MENU_ITEM	5

#define pinClockA		12
#define pinClockB		13
#define PIN_LED			13
#define PIN_POWER		13
#define PIN_CLOCKLIGHT	11
#define PIN_BACKLIGHT	10
#define PIN_BATTERY		A3

void setup()
{
	Serial.begin(9600);
	Serial.print("AT");
	delay(1000);
	while (Serial.available())
		Serial.read();
	Serial.print("AT+NAMEuferclock");
	delay(1000);
	while (Serial.available())
		Serial.read();
	Serial.print("AT+PIN1134");
	delay(1000);
	while (Serial.available())
		Serial.read();
	/*
	lcd.createChar(1, c_up);
	lcd.createChar(2, c_down);
	lcd.createChar(3, c_select);
	lcd.begin(16, 2);
	lcd.clear();
	*/

	pinMode(PIN_BACKLIGHT, OUTPUT);
	analogWrite(PIN_BACKLIGHT, 1);

	pinMode(pinClockA, OUTPUT);
	digitalWrite(pinClockA, LOW);
	pinMode(pinClockB, OUTPUT);
	digitalWrite(pinClockB, LOW);
	//pinMode(PIN_LED, OUTPUT);
	//digitalWrite(PIN_LED, LOW);
	//pinMode(PIN_POWER, OUTPUT);
	//digitalWrite(PIN_POWER, LOW);
	pinMode(PIN_CLOCKLIGHT, OUTPUT);
	digitalWrite(PIN_CLOCKLIGHT, LOW);

	/*
	lcd.setCursor(0, 0);
	lcd.print("               ");
	lcd.setCursor(0, 1);
	lcd.print("Hi from UferUhr");
	delay(1000);
	lcd.setCursor(0, 0);
	lcd.print("Hi from UferUhr");
	lcd.setCursor(0, 1);
	lcd.print("---------------");
	delay(1000);
	lcd.setCursor(0, 0);
	lcd.print("---------------");
	lcd.setCursor(0, 1);
	lcd.print("               ");
	delay(1000);
	*/

	if (!rtc.begin())
	{
		doDebugOutput("Couldn't find RTC.", 0, 1);
	}

	if (!rtc.isrunning())
	{
		doDebugOutput("RTC is NOT running.", 0, 1);
	}

	/*
	lcd.print("               ");
	*/

	// get twrminutes from a safe place
	twrMinutes = rtc.readnvram(0) << 8;
	twrMinutes |= rtc.readnvram(1);
	twrStep = rtc.readnvram(2);
	if ((twrMinutes < 0) || (twrMinutes >= (12 * 60)))
	{
		// if tower clock is not reasonable:
		// assume the tower clock is in sync with current time
		DateTime now = rtc.now();
		twrMinutes = now.minute() + (now.hour() % 12) * 60;
		if (isSommerTime(now.hour(), now.day(), now.month(), now.year(), now.dayOfTheWeek()))
		{
			twrMinutes += 60;
			if (twrMinutes >= (12 * 60))
				twrMinutes -= (12 * 60);
		}
	}
	if (twrStep < 0)
	{
		twrStep = 0;
	}

	dcf.Start();
}

void loop()
{
	static unsigned long lastTowerMillis = 0;
	DateTime now = rtc.now();

	time_t time = dcf.getTime();
	if (time != 0)
	{
		Serial.print((int) time, 10);
		Serial.println();
		//rtc.adjust();
	}

	// cheap astro schaltung:
	if (now.month() < 6)
	{
		if ((now.hour() < (4 + (6 - now.month()) * 4 / 6)) || (now.hour() > (21 - (6 - now.month()))))
			twrLightSwitch = 1;
		else
			twrLightSwitch = 0;
	}
	else if (now.month() > 6)
	{
		if ((now.hour() < (4 + (now.month() - 6) * 4 / 6)) || (now.hour() > (21 - (now.month() - 6))))
			twrLightSwitch = 1;
		else
			twrLightSwitch = 0;
	}
	else
	{
		if ((now.hour() < 4) || (now.hour() > 21))
			twrLightSwitch = 1;
		else
			twrLightSwitch = 0;
	}

	if (isSommerTime(now.hour(), now.day(), now.month(), now.year(), now.dayOfTheWeek()))
	{
		now = DateTime(now.unixtime() + (60 * 60));
	}

	/*
	lcd.setCursor(0, 0);
	*/

	static int oldTwrMinutes = 0;
	if (twrMinutes != oldTwrMinutes)
	{
		rtc.writenvram(0, (twrMinutes >> 8) & 0xff);
		rtc.writenvram(1, twrMinutes & 0xff);
		rtc.writenvram(2, twrStep & 0xff);
		oldTwrMinutes = twrMinutes;
	}

	//if (handleLCD(now))
	{
		// this also handles rollovers
		unsigned long currentMillis = millis();
		if ((unsigned long)(currentMillis - lastTowerMillis) >= 500)
		{
			lastTowerMillis = currentMillis;
			handleTowerClock(now);
		}
	}

	if (twrLightSwitch > 0)
		analogWrite(PIN_CLOCKLIGHT, twrLightValue);
	else
		digitalWrite(PIN_CLOCKLIGHT, 0);

	//digitalWrite(PIN_POWER, HIGH);
	delayMicroseconds(100);
	//digitalWrite(PIN_POWER, LOW);

	HandleBluetooth();
}

void handleTowerClock(DateTime now)
{
	// convert now into minutes
	int rtcMinutes = now.minute() + (now.hour() % 12) * 60;

	if (twrReset)
	{
		// use a fixed time if we reset the clock to 09:05
		rtcMinutes = 9 * 60 + 5;
	}

	// increase twrMinutes as long as there is a difference between rtcMinutes and twrMinutes
	int diffMinutes = (twrMinutes < rtcMinutes) ? (twrMinutes + (12 * 60) - rtcMinutes) : (twrMinutes - rtcMinutes);
	//if (twrMinutes != rtcMinutes)
	//if (1 == 1)
	if (diffMinutes >(2 * 60))
	{
		// move clock forward
		if (twrStep == 0)
		{
			digitalWrite(pinClockA, HIGH);
		}
		else if (twrStep == 1)
		{
			digitalWrite(pinClockA, LOW);
			twrMinutes++;
			if (twrMinutes >= (12 * 60))
				twrMinutes = 0;
		}
		else if (twrStep == 2)
		{
			digitalWrite(pinClockB, HIGH);
		}
		else if (twrStep == 3)
		{
			digitalWrite(pinClockB, LOW);
			twrMinutes++;
			if (twrMinutes >= (12 * 60))
				twrMinutes = 0;
		}
		twrStep++;
		if (twrStep > 3)
			twrStep = 0;
	}
}

void HandleBluetooth()
{
	if (Serial.available())
	{
		char buffer[32];

		Serial.println("Hello from UferUhr. (Send 's', 't' or 'l')");

		DateTime now = rtc.now();
		char chr = Serial.read();
		if (chr == 's')
		{
			sprintf(buffer, "%02d.%02d.%04d %02d:%02d:%02d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
			Serial.println("Current Date/Time (w/o DST):");
			Serial.println(buffer);
			memset(buffer, 0, sizeof(buffer));
			Serial.readBytesUntil('\n', buffer, sizeof(buffer));
			Serial.print(strlen(buffer), DEC);
			Serial.print(": ");
			Serial.println(buffer);
			if (strlen(buffer) >= 20)
			{
				int DD = atoi(buffer + 1);
				int MM = atoi(buffer + 4);
				int YY = atoi(buffer + 7);
				int hh = atoi(buffer + 12);
				int mm = atoi(buffer + 15);
				int ss = atoi(buffer + 18);
				rtc.adjust(DateTime(YY, MM, DD, hh, mm, ss));
				now = rtc.now();
				sprintf(buffer, "%02d.%02d.%04d %02d:%02d:%02d", now.day(), now.month(), now.year(), now.hour(), now.minute(), now.second());
				Serial.println("New Date/Time (w/o DST):");
				Serial.println(buffer);
			}
			else
			{
				Serial.println("To set Date/Time: s DD.MM.YYYY HH:MM:SS");
			}
		}
		else if (chr == 't')
		{
			sprintf(buffer, "%02d.%02d", twrMinutes / 60, twrMinutes % 60);
			Serial.println("TowerTime:");
			Serial.println(buffer);
			memset(buffer, 0, sizeof(buffer));
			Serial.readBytesUntil('\n', buffer, sizeof(buffer));
			Serial.print(strlen(buffer), DEC);
			Serial.print(": ");
			Serial.println(buffer);
			if (strlen(buffer) >= 5)
			{
				int hh = atoi(buffer + 1);
				int mm = atoi(buffer + 4);
				twrMinutes = hh * 60 + mm;
				sprintf(buffer, "%02d.%02d", twrMinutes / 60, twrMinutes % 60);
				Serial.println("TowerTime:");
				Serial.println(buffer);
			}
			else
			{
				Serial.println("To set TowerTime: t HH:MM");
			}
		}
		else if (chr == 'l')
		{
			sprintf(buffer, "%02d", twrLightValue);
			Serial.println("TowerLight:");
			Serial.println(buffer);
			memset(buffer, 0, sizeof(buffer));
			Serial.readBytesUntil('\n', buffer, sizeof(buffer));
			Serial.print(strlen(buffer), DEC);
			Serial.print(": ");
			Serial.println(buffer);
			if (strlen(buffer) >= 1)
			{
				int l = atoi(buffer + 1);
				if (l > 0) {
					twrLightValue = l;
					twrLightSwitch = 1;
				}
				else
				{
					twrLightSwitch = 0;
				}
				sprintf(buffer, "%02d", twrLightValue);
				Serial.println("TowerLight:");
				Serial.println(buffer);
			}
			else
			{
				Serial.println("To set TowerLight: l 0..255");
			}
		}
	}
}

void doDebugOutput(const char *line, int row, int col)
{
//	lcd.setCursor(row, col);
//	lcd.print(line);
	Serial.println(line);
//	delay(1000);
}

/*
int handleLCD(DateTime now)
{
	static int leftIndex = 0;
	static int oldButtonPressed = KEYPAD_NONE;
	static byte mode = 0;
	static int hh, mm, ss;
	static int DD, MM, YY;
	static int menu_item = 0;
	char buffer[20]; // 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 4´+ 1
	int buttonPressed;

	if (mode == CLOCK_DISPLAY)
	{
		// rtc
		sprintf(buffer, "%02d:%02d:%02d %s     ", now.hour() % 12, now.minute(), now.second(), (now.hour() >= 12) ? "PM" : "AM");
		lcd.setCursor(0, 0);
		lcd.print(buffer + leftIndex);
		// tower clock and bat
		sprintf(buffer, "Twr %02d:%02d", twrMinutes / 60, twrMinutes % 60);
		lcd.setCursor(0, 1);
		lcd.print(buffer);
	}
	else if (mode == CLOCK_MENU)
	{
		// menu
		if (menu_item == 0)
		{
			lcd.setCursor(0, 0 - menu_item);
			lcd.print("  EXIT MENU     ");
		}
		if ((menu_item == 0) || (menu_item == 1))
		{
			lcd.setCursor(0, 1 - menu_item);
			lcd.print("  ENTER CLOCK   ");
		}
		if ((menu_item == 1) || (menu_item == 2))
		{
			lcd.setCursor(0, 2 - menu_item);
			lcd.print("  RESET CLOCK   ");
		}
		if ((menu_item == 2) || (menu_item == 3))
		{
			lcd.setCursor(0, 3 - menu_item);
			lcd.print("  ADJUST RTC    ");
		}
		if ((menu_item == 3) || (menu_item == 4))
		{
			lcd.setCursor(0, 4 - menu_item);
			lcd.print("  DEBUG CLOCK   ");
		}
		if (menu_item == 4)
		{
			lcd.setCursor(0, 5 - menu_item);
			lcd.print("                ");
		}
		lcd.setCursor(0, 0);
		lcd.print(">");
	}
	else if (mode == CLOCK_RESET)
	{
		// set clock to 9:05 and exit
		lcd.setCursor(0, 0);
		lcd.print("Reset to 9:05   ");
		lcd.setCursor(0, 1);
		lcd.print("                ");
	}
	else if (mode == CLOCK_TOWER)
	{
		// enter the current tower clock time
		lcd.setCursor(0, 0);
		lcd.print("Twr Clock: ");
		sprintf(buffer, "%02d:%02d", twrMinutes / 60, twrMinutes % 60);
		lcd.setCursor(11, 0);
		lcd.print(buffer);

		lcd.setCursor(0, 1);
		lcd.print("                ");

		if (leftIndex == 0)
		{
			lcd.setCursor(11, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else
		{
			lcd.setCursor(14, 1);
			lcd.write(1);
			lcd.write(2);
		}
	}
	else if (mode == CLOCK_ADJUST)
	{
		lcd.setCursor(0, 0);
		sprintf(buffer, "%02d:%02d:%02d %02d.%02d.%04d", hh, mm, ss, DD, MM, YY);
		if (leftIndex < 3)
		{
			lcd.print(buffer);
		}
		else
		{
			lcd.print(buffer + 3);
		}
		lcd.setCursor(0, 1);
		lcd.print("                ");

		if (leftIndex == 0)
		{
			lcd.setCursor(0, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else if (leftIndex == 1)
		{
			lcd.setCursor(3, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else if (leftIndex == 2)
		{
			lcd.setCursor(6, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else if (leftIndex == 3)
		{
			lcd.setCursor(6, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else if (leftIndex == 4)
		{
			lcd.setCursor(9, 1);
			lcd.write(1);
			lcd.write(2);
		}
		else if (leftIndex == 5)
		{
			lcd.setCursor(12, 1);
			lcd.write(1);
			lcd.write(1);
			lcd.write(2);
			lcd.write(2);
		}
	}
	else if (mode == CLOCK_DEBUG)
	{
		while (lcd.button() == KEYPAD_SELECT)
		{
			delay(100);
		}

		lcd.setCursor(0, 0);
		lcd.print("12 V on Pin A");
		lcd.setCursor(0, 1);
		lcd.print("                ");

		digitalWrite(pinClockA, HIGH);
		digitalWrite(PIN_LED, HIGH);

		while (lcd.button() != KEYPAD_SELECT)
		{
			delay(100);
		}

		digitalWrite(pinClockA, LOW);
		digitalWrite(PIN_LED, LOW);

		delay(1000);

		while (lcd.button() == KEYPAD_SELECT)
		{
			delay(100);
		}

		lcd.setCursor(0, 0);
		lcd.print("12 V on Pin B");
		lcd.setCursor(0, 1);
		lcd.print("                ");

		digitalWrite(pinClockB, HIGH);
		digitalWrite(PIN_LED, HIGH);

		while (lcd.button() != KEYPAD_SELECT)
		{
			delay(100);
		}

		digitalWrite(pinClockB, LOW);
		digitalWrite(PIN_LED, LOW);

		mode = CLOCK_DISPLAY;
		analogWrite(10, 1);
	}

	buttonPressed = lcd.button();
	if (buttonPressed != oldButtonPressed)
	{
		if (buttonPressed == KEYPAD_LEFT)
		{
			if (leftIndex > 0)
				leftIndex--;
		}
		else if (buttonPressed == KEYPAD_RIGHT)
		{
			if (mode == CLOCK_DISPLAY)
			{
				if (leftIndex < 3)
					leftIndex++;
			}
			else if (mode == CLOCK_ADJUST)
			{
				if (leftIndex < 5)
					leftIndex++;
			}
			else if (mode == CLOCK_TOWER)
			{
				if (leftIndex < 1)
					leftIndex++;
			}
		}
		else if (buttonPressed == KEYPAD_UP)
		{
			if (mode == CLOCK_ADJUST)
			{
				if (leftIndex == 0)
				{
					if (hh < 23)
						hh++;
				}
				else if (leftIndex == 1)
				{
					if (mm < 59)
						mm++;
				}
				else if (leftIndex == 2)
				{
					if (ss < 59)
						ss++;
				}
				else if (leftIndex == 3)
				{
					if (DD < 31)
						DD++;
				}
				else if (leftIndex == 4)
				{
					if (MM < 12)
						MM++;
				}
				else if (leftIndex == 5)
				{
					if (YY < 2100)
						YY++;
				}
			}
			else if (mode == CLOCK_MENU)
			{
				menu_item--;
				if (menu_item < 0)
					menu_item = 0;
			}
			else if (mode == CLOCK_TOWER)
			{
				if (leftIndex == 0)
					twrMinutes += 60;
				else if (leftIndex == 1)
					twrMinutes += 1;
				if (twrMinutes >= (12 * 60))
					twrMinutes -= 12 * 60;
			}
		}
		else if (buttonPressed == KEYPAD_DOWN)
		{
			if (mode == CLOCK_ADJUST)
			{
				if (leftIndex == 0)
				{
					if (hh > 0)
						hh--;
				}
				else if (leftIndex == 1)
				{
					if (mm > 0)
						mm--;
				}
				else if (leftIndex == 2)
				{
					if (ss > 0)
						ss--;
				}
				else if (leftIndex == 3)
				{
					if (DD > 1)
						DD--;
				}
				else if (leftIndex == 4)
				{
					if (MM > 1)
						MM--;
				}
				else if (leftIndex == 5)
				{
					if (YY > 2015)
						YY--;
				}
			}
			else if (mode == CLOCK_MENU)
			{
				menu_item++;
				if (menu_item > MAX_MENU_ITEM)
					menu_item = MAX_MENU_ITEM;
			}
			else if (mode == CLOCK_TOWER)
			{
				if (leftIndex == 0)
					twrMinutes -= 60;
				else if (leftIndex == 1)
					twrMinutes -= 1;
				if (twrMinutes < 0)
					twrMinutes += 24 * 60;
			}
		}
		else if (buttonPressed == KEYPAD_SELECT)
		{
			leftIndex = 0;
			if (mode == CLOCK_DISPLAY)
			{
				// switch to time set mode
				menu_item = 0;
				mode = CLOCK_MENU;
				analogWrite(10, 100);
			}
			else if (mode == CLOCK_MENU)
			{
				if (menu_item == 0)
				{
					// just show the current RTC
					mode = CLOCK_DISPLAY;
					analogWrite(10, 1);
				}
				else if (menu_item == 1)
				{
					// enter the hour/minutes the tower clock displays
					leftIndex = 0;
					mode = CLOCK_TOWER;
				}
				else if (menu_item == 2)
				{
					// set tower clock to 9:05
					mode = CLOCK_RESET;
				}
				else if (menu_item == 3)
				{
					leftIndex = 0;
					now = rtc.now();
					hh = now.hour();
					mm = now.minute();
					ss = now.second();
					DD = now.day();
					MM = now.month();
					YY = now.year();
					mode = CLOCK_ADJUST;
				}
				else if (menu_item == 4)
				{
					// steady 12 V
					mode = CLOCK_DEBUG;
				}
			}
			else if (mode == CLOCK_TOWER)
			{
				mode = CLOCK_DISPLAY;
				analogWrite(10, 1);
			}
			else if (mode == CLOCK_RESET)
			{
				mode = CLOCK_DISPLAY;
				analogWrite(10, 1);
				twrReset = 1;
			}
			else if (mode == CLOCK_ADJUST)
			{
				// set time accordingly and switch mode
				rtc.adjust(DateTime(YY, MM, DD, hh, mm, ss));
				mode = CLOCK_DISPLAY;
				analogWrite(10, 1);
			}
			else if (mode == CLOCK_DEBUG)
			{
				mode = CLOCK_DISPLAY;
				analogWrite(10, 1);
			}
		}
		oldButtonPressed = buttonPressed;
	}
	delay(10);

	// don't move clock if we just enter the current tower clock time
	return (mode != CLOCK_TOWER);
}
*/


/******************************************************************************************/
/******************************************************************************************/
/***  Filter a 24 hour cycle to 12h am/pm. Detect (MEZ/MESZ) Standard and Daylight Saving Time (DST)  ***/
/***  Create a 12 hour cycle with am/pm  ***/
boolean isSommerTime(int hour, int day, int month, int year, int dow)
{
	boolean pm = false; // Set to am per default
	boolean dst = false;
	
	/***  Switch to a 12 hour display  ***/
	if (hour > 12)
	{
		// 12h am/pm
		hour -= 12;
		pm = true;
	}
	/******************************************************************************************/
	/***  Determine the Daylight Saving Time DST. In Germany it is the last Sunday in March and in October  ***/
	/***  In March at 2:00am the time will be turned forward to 3:00am and in  ***/
	/***  October at 3:00am it will be turned back to 2:00am and repeated as 2A and 2B  ***/
	/******************************************************************************************/
	/***    Generally checking the full month and determine the DST flag is an easy job  ***/
	if ((month <= 2) || (month >= 11))
		dst = false;                                   // Winter months
	if ((month >= 4) && (month <= 9))
		dst = true;                                    // Summer months
	/***  Detect the beginning of the DST in March and set DST = 1  ***/
	if ((month == 3) && ((day - dow) >= 25)) {            // Begin of summer time
		if (((pm == false) && (hour >= 2)) || (pm == true))  // MESZ – 1 hour
			dst = true;
	}
	/***  Still summer months time DST beginning of October, so easy to determine  ***/
	if ((month == 10) && ((day - dow) < 25))
		dst = true;                                    // Summer months anyway until 24th of October
	/***  Test the begin of the winter time in October and set DST = 0  ***/
	if ((month == 10) && ((day - dow) >= 25))
	{           // Test the begin of the winter time
		if (((pm == false) && (hour >= 2)) || (pm == true))
		{
			// -1 since the RTC is running in GMT+1 only    
			dst = false;
			Serial.println("We have winter time");
		}
		else
		{
			dst = true;
			Serial.println("A good day! We have summer time");
		}
	}

	return dst;
}

Credits

MetaView

MetaView

8 projects • 15 followers

Comments