It’s minimalist, but it’s done.
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]]);
}