Well I finally soldered it all up after finalising the board layout and ironing out bugs in the software.
Final Schematic:
Final Board Layout:
Here are the parts:
BOM:
1 x Perfboard
1 x ATtiny44 MCU
17 x LEDs
2 x switches
2 x 10K resistors
2 x 7 pin female headers
2 x 12 pin female headers
1 x 10 pin female header
1 x 3 pin connector
Total cost is about £2.50 (if you buy the items in bulk just to have them available – it would probably cost a bit more if just buying the individual items as a one off for this project).
I added 3 additional strips of machined female header pins for the LEDs, (not shown in the above picture, so I could play about with what LEDs are used, rather than being stuck with what is soldered in place.
Here is the rear of the board showing the wiring, solid wires are legs from cut off LEDs soldered in other projects, the blue wire is Kynar 30awg wrapping wire.
I also had to strip out all of the debug code from the software as it was too big for the ATtiny44, it would be fine on an ATtiny84, I still wired up the TX connection should I decide to use an 84 and still want to see debug output via TinyDebugSerial.
Final version of the code (all of the previous debug messages have been removed):
#include <Bounce.h>
#define TONEPIN 3 // digital pin 3 (pa7) used as tone output
#define BUTTON1 1 // digital pin 0 (pb1) used to set clock
#define BUTTON2 4 // digital pin 6 (pa6) used to set clock
int masterClock = 0;
int lastsec = 0;
int seconds = 0;
int minutes = 0;
int hours = 0;
int hms = 0;
int sw1 = 0;
int sw2 = 0;
int both = 0;
boolean sw1on = false;
boolean sw2on = false;
boolean bothon = false;
const int nPins = 5;
const int pins[] = {6, 7, 8, 9, 10};
const int clock[17][2] = { {6, 7}, {6, 8}, {7, 8}, {8, 9}, {8, 10}, {9, 10}, // Seconds LEDs
{7, 6}, {8, 6}, {8, 7}, {9, 8}, {10, 8}, {10, 9}, // Minutes LEDs
{9, 6}, {6, 9}, {6, 10}, {10, 7}, {7, 10} }; // Hours LEDs
Bounce button1 = Bounce(BUTTON1, 5); // set 5ms debounce for BUTTON1
Bounce button2 = Bounce(BUTTON2, 5); // set 5ms debounce for BUTTON2
void setup() {
attachInterrupt(0, tick, RISING); // call ISR on rising input to INT0 (digital pin 2)
pinMode(TONEPIN, OUTPUT); // setup tonepin for output
tone(TONEPIN, 8000); // generate 8000Hz tone on digital pin 4 to act as clock input for ISR
pinMode(BUTTON1, INPUT); // setup button1 for input
pinMode(BUTTON2, INPUT); // setup button2 for input
}
void tick() { // Interrupt Service Routine to generate 1 second “tick”
masterClock++; // increment clock counter
if (masterClock == 8000) { // 8000 reached (8MHz) 16000 (16MHz)
seconds++; // increment seconds
masterClock = 0; // reset masterClock
}
}
void turnon(int led) {
int Vcc = clock[led][0];
int Gnd = clock[led][1];
pinMode(Vcc, OUTPUT);
pinMode(Gnd, OUTPUT);
digitalWrite(Vcc, HIGH);
digitalWrite(Gnd, LOW);
}
void alloff() {
for (int i = 0; i < nPins; i++) {
digitalWrite(pins[i], LOW);
pinMode(pins[i], INPUT);
}
}
void showtime(int Value, int Width, int Offset) {
int mask;
for (int i = 0; i < Width; i++) {
mask = 1 << i; // shift bit mask by current position
if (HIGH && (Value & mask)) { // check if LED should be lit
turnon(Offset + i); // turn on the LED
alloff();
}
}
}
void loop() {
// Check if buttons have been pressed
button1.update(); // update button1
button2.update(); // update button2
sw1 = button1.read(); // button 1 state
sw2 = button2.read(); // button 2 state
both = sw1 && sw2; // both state
if (sw1 == 0) sw1on = false;
if (sw2 == 0) sw2on = false;
if (both == 0) bothon = false;
// toggle setup states if both buttons are pressed and then released together
if (both && !bothon) {
if (hms == 0) hms=1; // toggle hours/minutes/seconds to hours if entering setup mode
else hms = 0; // toggle hours/minutes/seconds to off if leaving setup mode
bothon = true; // set flag for both buttons on
}
// set hours(1), minutes(2), or seconds(3)
if ((hms != 0) && sw1 && !sw1on && !bothon) {
hms++; // toggle hours/minutes/seconds
if (hms == 4) hms = 1; // reset back to hours if hms goes beyond seconds
sw1on = true;
}
if ((hms != 0) && sw2 && !sw2on && !bothon) {
if (hms == 1) {
hours++;
if (hours == 24) hours = 0;
}
if (hms == 2) {
minutes++;
if (minutes == 60) minutes = 0;
}
if (hms == 3) {
seconds = 0;
}
sw2on = true;
}
// Adjust the time based on seconds passed
if (seconds == 60) {
minutes++; // increment minutes when seconds reach 60
if (minutes == 60) {
hours++; // increment hours when minutes reach 60
if (hours == 24) {
hours = 0; // reset hours when hours reaches 24
}
minutes = 0; // reset minutes when minutes reaches 60
}
seconds = 0; // reset seconds when seconds reaches 60
}
showtime(seconds, 6, 0); // Display seconds (LEDs 0-5)
showtime(minutes, 6, 6); // Display minutes (LEDs 6-11)
showtime(hours, 5, 12); // Display hours (LEDs 12-16)
}