Introduction: 3D Printed Digital Timer

About: Technology is subversive.

I do speaking gigs via zoom and wanted an easy way to track how long I'd been talking. But I wanted a physical object on my standing desk rather than anything on the computer screen, as I'm always bouncing from one screen to another. So I made this super simple design: single button to start/reset the timer. Big bold LED display. I used a Teensy I had lying around but any Arduino board you can fit into the casing will do.

Supplies

What you'll need:

3D Printer (or access to one)

Arduino, Teensy, or equivalent

MAX7219 8x8 LED display board

Pushbutton switch

10K Resistor

Breadboard & connectors

Hot glue gun

Step 1: Print the Case

Print the main case by importing the attached .stl file into Blender, making any adjustments, exporting to a slicer, and printing. If none of that sentence made sense, there's a nice tutorial here about how to go from an .stl file to final print.

Step 2:

Print the reset button. I found the fit of the two housing pieces very tight and had to do a bit of sanding to make them fit. No biggie, but if you're of a precise nature, you may want to shave a half a millimeter off one of those interfaces.

Step 3: Wire Up Your Arduino

The MAX7219 8x8 LED Matrix unit has five pins: power, ground, DIN, CS, and CLK. On a Teensy, you want to wire up the power to the 5 volt VIN. Wire the data out (DOUT) pin 11 from the Arduino to the Data In (DIN) pin on the Max, The Chip Select or CS pin is 10, and the clock signal comes from pin 13.


Step 4: The Code

/*
Stopwatch project displays time elapsed in minutes on a MAX7219 8x8 LED display. Button to start / reset to zero. Displays two digits, the second dropped down a row, and a dot that pulses to let you know it's 
working.  
Code is Creative Commons non commercial share and share alike. 
*/


#include <MaxMatrix.h>
#include <avr/pgmspace.h>


//Define the array of alphanumeric characters the 8x8 Matrix can display
//You actually only use the digits 0-9
//This array maps every LED on the board as on or off depending on 
//which character you want to display
PROGMEM prog_uchar CH[] = {
3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // space
1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
5, 8, B00111110, B01001001, B01010101, B01011101, B00001110, // @
4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
4, 8, B01000110, B01001001, B01001001, B00110010, B00000000, // S
5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \ backslash
2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
3, 8, B00000010, B00000001, B00000010, B00000000, B00000000, // hat
4, 8, B01000000, B01000000, B01000000, B01000000, B00000000, // _
2, 8, B00000001, B00000010, B00000000, B00000000, B00000000, // `
4, 8, B00100000, B01010100, B01010100, B01111000, B00000000, // a
4, 8, B01111111, B01000100, B01000100, B00111000, B00000000, // b
4, 8, B00111000, B01000100, B01000100, B00101000, B00000000, // c
4, 8, B00111000, B01000100, B01000100, B01111111, B00000000, // d
4, 8, B00111000, B01010100, B01010100, B00011000, B00000000, // e
3, 8, B00000100, B01111110, B00000101, B00000000, B00000000, // f
4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
4, 8, B01111111, B00010000, B00101000, B01000100, B00000000, // k
3, 8, B01000001, B01111111, B01000000, B00000000, B00000000, // l
5, 8, B01111100, B00000100, B01111100, B00000100, B01111000, // m
4, 8, B01111100, B00000100, B00000100, B01111000, B00000000, // n
4, 8, B00111000, B01000100, B01000100, B00111000, B00000000, // o
4, 8, B11111100, B00100100, B00100100, B00011000, B00000000, // p
4, 8, B00011000, B00100100, B00100100, B11111100, B00000000, // q
4, 8, B01111100, B00001000, B00000100, B00000100, B00000000, // r
4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
3, 8, B00000100, B00111111, B01000100, B00000000, B00000000, // t
4, 8, B00111100, B01000000, B01000000, B01111100, B00000000, // u
5, 8, B00011100, B00100000, B01000000, B00100000, B00011100, // v
5, 8, B00111100, B01000000, B00111100, B01000000, B00111100, // w
5, 8, B01000100, B00101000, B00010000, B00101000, B01000100, // x
4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
3, 8, B01100100, B01010100, B01001100, B00000000, B00000000, // z
3, 8, B00001000, B00110110, B01000001, B00000000, B00000000, // {
1, 8, B01111111, B00000000, B00000000, B00000000, B00000000, // |
3, 8, B01000001, B00110110, B00001000, B00000000, B00000000, // }
4, 8, B00001000, B00000100, B00001000, B00000100, B00000000, // ~
};


//Check your board, these are the DOUT, CS, and CLK pins for a TeensyDuino.
int data = 11;    // 8, DIN pin of MAX7219 module
int load = 10;    // 9, CS pin of MAX7219 module
int clock = 13;  // 10, CLK pin of MAX7219 module


int maxInUse = 1;    //change this variable to set how many MAX7219's you'll use


MaxMatrix m(data, load, clock, maxInUse); // define module as m

byte buffer[10];       // for writing the numbers
long lastTime = 0;     //for keeping track of time
long minutes = 0s;     // the minutes elapsed that the clock displays
int secondCounter = 0; // a counter to set up the faux-second counter


int inPin = 2;         // the number of the Input pin for the reset switch


int state = HIGH;      // the start state of the input pin
int reading;           // will hold the current reading from the input pin
int previous = LOW;    // stores the previous reading from the input pin


// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
unsigned long time = 0;           // the last time the output pin was toggled
unsigned long debounce = 200UL;   // the debounce time, increase if the output flickers


// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

char string1[] = "00";


void setup(){
  m.init();               // module initialize
  m.setIntensity(4);      // dot matix intensity 0-15
  Serial.begin(9600);     // serial communication initialize
  pinMode(inPin,  INPUT); // initialize the reset switch pin
}




void loop()
{


//This creates the flashing dots upper right 
//that faux-count seconds. They just let you know it's
//counting...  ¯\_(ツ)_/¯
    if(secondCounter < 500){
      m.setDot(7,0,LOW);
      m.setDot(6,0,HIGH);
      secondCounter++;    
        };


      if(secondCounter >= 500){
        // Erase one dot and light up the other
          m.setDot(7,0,HIGH);
          m.setDot(6,0,LOW);
          secondCounter++;
          if(secondCounter > 1000){
            secondCounter = 0;
          }

    }


// Here we count milliseconds up to 60000 = 1 minute and store the previous time
   if(millis()-lastTime > 60000){
    minutes++;
    lastTime = millis();
  }
//ltoa converts long variable minutes to a string, loads it into the string1 array
  ltoa(minutes,string1,10); 
  printString(string1);


//Let's just check that the reset button hasn't been pressed  
   reading = digitalRead(inPin);
   Serial.println(reading);
//And if it has, reset the time to 0 and the lastTime to now.    
   if(reading == HIGH){
   lastTime = millis();
    minutes = 0;
    char string1[] = "0";
//Flash the four dots in the upper right so user knows the push button is working
      m.setDot(7,0,HIGH);
      m.setDot(7,1,HIGH);
      m.setDot(6,1,HIGH);
      m.setDot(6,0,HIGH);
//Now clear the screen and display the 0 minute start state
          m.clear();
    ltoa(minutes,string1,10); 
    printString(string1);    
   }
//And go back to the top...    
};

//Print the character by using that fancy array in progmem which tells each
//LED whether it's on or off
void printString(char* s)
{
  int col = 0;
  int y = 0;
  while (*s != 0)
  {
    if (*s < 32) continue;
    char c = *s - 32;
    memcpy_P(buffer, CH + 7*c, 7);
    m.writeSprite(col, y, buffer);
   
    m.setColumn(col + buffer[0], 0);
    col += buffer[0];
    s++;
    y++;
  }
 }

Step 5: Final Assembly

Once you've tested the circuit on the breadboard, wire your pushbutton up in the housing. I mounted it on a bit of foam to give a nice bounce to the reset switch, passing the wires directly through the foam.


I mounted the Teensy to a mini breadboard and embedded it in a glob of hot glue to keep the wires in place. Solder if you like, but I'm a fan of the temporary nature of hot glue so I can easily repurpose a board when I'm bored of the project it's in. Ha ha. The hot glue also allows you a degree of flexibility where you mount the breadboard inside the box -- it's a bit cramped getting the guts all tucked in and tidy.

And boom. That's it!