My clock is done

On October 26, 2015, in General, by Neil Stevens

It’s minimalist, but it’s done.

Clock Completed

I posted this earlier on Twitter but after that I decided I needed to tape up the set of plugs connecting the display for ease of plugging/unplugging. That turned out to matter when I realized I’d assembled the case inside out, weirdly enough. So I had to take it all apart and put it back together, which meant unplugging and replugging the display!

I ended up putting no buttons on it. I wrote a little python program for setting the time.

Following is the final code for the Arduino sketch, which drives the display, which is a row of four 8×8 LED grids, driven by an array of four MAX7219 chips. The code also drives the DS3231-based Real Time Clock daughterboard with battery backup.

Though while I’m listing components, the case is a neat little acrylic thing, and the main board is a SainSmart Arduino Uno clone.

On the software end I used the Bounce2, LedControl, and RTClib libraries, with RTClib depending on the Wire library. Well, I had use Bounce2 until I took out the buttons, then just commented all of that out.

/*
#include "Bounce2.h"
*/
#include "LedControl.h"
#include "RTClib.h"
#include "Wire.h"

const byte lowA[8] = {B00000000, B00000000, B00000000, B01111100, B10000010, B10000010, B10000010, B01111101};
const byte capB[8] = {B11111110, B10000001, B10000001, B10000001, B11111110, B10000001, B10000001, B11111110};
const byte lowE[8] = {B00000000, B00000000, B00000000, B01111110, B10000001, B11111111, B10000000, B01111110};
const byte lowG[8] = {B00000000, B00000000, B01111110, B10000001, B10000001, B01111110, B00000001, B01111110};
const byte capH[8] = {B10000001, B10000001, B10000001, B11111111, B10000001, B10000001, B10000001, B10000001};
const byte lowR[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B10000000, B10000000, B10000000};
const byte lowS[8] = {B00000000, B00000000, B00000000, B01111111, B10000000, B01111110, B00000001, B01111110};
const byte lowU[8] = {B00000000, B00000000, B00000000, B10000001, B10000001, B10000001, B10000011, B01111101};
const byte capW[8] = {B10000001, B10000001, B10000001, B10010001, B10010001, B10010001, B10010001, B01111110};
const byte space[8] = {0, 0, 0, 0, 0, 0, 0, 0};

const byte digits[10][8] = {
	{ B0111110, B1100011, B1100111, B1101011, B1110011, B1100011, B0111110, 0},
	{ B0001110, B0011110, B0001110, B0001110, B0001110, B0001110, B0001110, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0110000, B1100000, B1111111, 0},
	{ B0111110, B1100011, B0000011, B0011110, B0000011, B1100011, B0111110, 0},
	{ B1100110, B1100110, B1100110, B1100110, B1111111, B0000110, B0000110, 0},
	{ B1111111, B1100000, B1111110, B0000011, B0000011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100000, B1111110, B1100011, B1100011, B0111110, 0},
	{ B1111111, B0000011, B0000110, B0001100, B0011100, B0011100, B0011100, 0},
	{ B0111110, B1100011, B1100011, B0111110, B1100011, B1100011, B0111110, 0},
	{ B0111110, B1100011, B1100011, B0111111, B0000011, B1100011, B0111110, 0}
};

const unsigned twentyFourHour = false;
LedControl lc = LedControl(11 /* data */, 12 /* clk */, 10 /* cs */, 4);
RTC_DS1307 RTC;
/*
Bounce brightnessButton = Bounce();
Bounce *buttonArray[1] = {&brightnessButton};
*/
boolean firstLoop = true;

void clearAll(LedControl lc) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.clearDisplay(i);
	}
}

void setAll(LedControl lc, const int x, const int y)
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setRow(i, x, y);
	}
}

void writeLetter(LedControl lc, const int digit, const byte letter[8], const boolean colon = false)
{
	for(int i = 0; i < 8; ++i) {
		int bits = letter[i];
		if(colon && (i == 2 || i == 4)) {
			bits |= B10000000;
		}
		lc.setRow(digit, i, bits);
	}
}

void setIntensity(LedControl lc, const int brightness) {
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.setIntensity(i, brightness);
	}
}

void setup()
{
	for(int i = 0; i < lc.getDeviceCount(); ++i) {
		lc.shutdown(i, false);
		lc.setIntensity(i, 0x0f);
		lc.clearDisplay(i);
	}

/*
	pinMode(1, INPUT_PULLUP);
	brightnessButton.attach(1);
*/

	writeLetter(lc, 3, capB);
	writeLetter(lc, 2, lowA);
	writeLetter(lc, 1, lowG);
	writeLetter(lc, 0, lowU);

	Serial.begin(9600);

	RTC.begin();
}

// returns array {hourTens, hourOnes, minuteTens, minuteOnes}
const byte *timeDigits(DateTime now) {
	static byte ret[4] = {9,9,9,9};
	byte hour = now.hour();
	if(twentyFourHour) {
		hour %= 24;
	} else {
		hour %= 12;
		if(hour == 0) {
			hour = 12;
		}
	}
	byte min = now.minute();

	ret[0] = hour / 10;
	ret[1] = hour % 10;
	ret[2] = min / 10;
	ret[3] = min % 10;
	return ret;
}

void loop()
{
	while(!RTC.isrunning()) {
		Serial.println("RTC not running.");
	}
	static unsigned short brightness;

	if(firstLoop) {
		brightness = 0xf;
		firstLoop = false;
	}

	while(Serial.available() > 0) {
		String cmd = Serial.readString();
		cmd.trim();
		if(cmd == "SET") {
			Serial.println("Reading date");

			while(Serial.available() < = 0) {
				delay(1);
			}
			String d = Serial.readString();
			d.trim();

			Serial.print("Setting date to '");
			Serial.print(d.c_str());
			Serial.println("'");

			Serial.println("Reading time");

			while(Serial.available() <= 0) {
				delay(1);
			}
			String t = Serial.readString();
			t.trim();

			Serial.print("Setting time to '");
			Serial.print(t.c_str());
			Serial.println("'");

			RTC.adjust(DateTime(d.c_str(), t.c_str()));
		} else if(cmd == "SEIZE") {
			Serial.println("YOU");
		}
	}

/*
	for(byte i = 0; i < 2; ++i) {
		buttonArray[i]->update();
	}

	if(brightnessButton.fell()) {
		brightness = (brightness + 1) % 0x10;
		setIntensity(lc, brightness);
	}
*/

	DateTime now = RTC.now();
	const byte *currentDigits = timeDigits(now);

	// Write the clock
	if(currentDigits[0] == 0) {
		writeLetter(lc, 3, space);
	} else {
		writeLetter(lc, 3, digits[currentDigits[0]]);
	}
	writeLetter(lc, 2, digits[currentDigits[1]]);
	writeLetter(lc, 1, digits[currentDigits[2]], (now.second() % 2) == 1);
	writeLetter(lc, 0, digits[currentDigits[3]]);
}
 

Comments are closed.