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]]); }