MAX7219 LED Clock with Arduino Nano

This is a small project that I created using MAX7219 8 Digit 7 Segment Displays to create a clock that shows the date and time. I used an DS1307 Real Time Clock for keeping time, and an Arduino Nano to bring it all together on a small breadboard (for now).

Components Required

MAX7219 8 digit 7 segment display x 2
TinyRTC I2C DS1307 RTC Module
Arduino Nano (can also be used on an Uno)
Breadboard
Connecting Wires

 

TinyRTC Issues

I ordered a TinyRTC I2C module from eBay, however, it did not come with a rechargeable LIM2032 battery but a standard CR2032 non rechargeable lithium battery. As the TinyRTC I2C DS1307 module has a charging circuit for charging lithium cells, this needed to be modified so that it would not charge a non rechargeable cell. I’d like to credit the website brainy-bits.com for the details below on how to modify the module to remove the charging circuitry and allow me to use the module with a standard CR2032 battery.

Modifying the TinyRTC

You need to remove the Diode at D1 and both the resistors at R6 and R4. You then need to create a solder (or wire bridge) between the pads of R6. If you need further advice with this, check out their original article which I found this information from here.

Now that we have modified the DS1307, we can attach it to the Arduino on the SPI headers, which on the Nano is located at A5 for SCL and A4 for SDA. Then you need to connect VCC to +5 Volt and GND to GND.

Attaching the MAX7219 strips

Then to attach our first MAX7219 with the following connections: Pin 9 is connected to the DataIn, Pin 8 is connected to the CLK and pin 7 is connected to LOAD. VCC goes to +5 Volt and GND to GND.

Our second MAX7219 display can connect to the first one, making sure that DataOut on the first module is connected to DataIn on the second module and that LOAD, CLK, VCC and GND are connected to each other.

Setting The Time

Now your circuit is built, we need to set the time on the DS1307. Once you have installed the required libraries (linked below), you can open the example sketch from the DS1307RTC for SetTime.

However, I have found that this sets the time 7 seconds too slow. This is because it uses the time that the program was compiled as its reference for time, and takes a few seconds to upload to the Arduino nano. This modified sketch won’t function correctly if you compile within the last 7 seconds of a minute, however this can be adjusted by changing the DEFINE Offset 7 below if you find that this isn’t correct for your needs.

Libraries

Time Library – https://github.com/PaulStoffregen/Time or download here
DS1307RTC Library – https://github.com/PaulStoffregen/DS1307RTC or download here
LedControl Library – https://github.com/wayoda/LedControl or download here

SetTime_with_Offset.ino

/*   FILENAME: SetTime_with_Offset.ino
 *   AUTHOR(s): Jessica at AccessiblePixel.com
 *   CONTACT: media@accessiblepixel.com
 *   LICENSE: 
 *     
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

#define OFFSET 7 // Offset for Adding Seconds

const char *monthName[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

tmElements_t tm;

void setup() {
  bool parse=false;
  bool config=false;

  // get the date and time the compiler was run
  if (getDate(__DATE__) && getTime(__TIME__)) {
    parse = true;
    // and configure the RTC with this info
    if (RTC.write(tm)) {
      config = true;
    }
  }

  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  delay(200);
  if (parse && config) {
    Serial.print("DS1307 configured Time=");
    Serial.print(tm.Hour);
    Serial.print(tm.Minute);
    Serial.print(tm.Second);
    Serial.print(", Date=");
    Serial.println(__DATE__);
  } else if (parse) {
    Serial.println("DS1307 Communication Error :-{");
    Serial.println("Please check your circuitry");
  } else {
    Serial.print("Could not parse info from the compiler, Time=\"");
    Serial.print(__TIME__);
    Serial.print("\", Date=\"");
    Serial.print(__DATE__);
    Serial.println("\"");
  }
}

void loop() {
}

bool getTime(const char *str)
{
  int Hour, Min, Sec;

  if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
  tm.Hour = Hour;
  tm.Minute = Min;
  tm.Second = Sec;
  tm.Second = tm.Second + OFFSET;
  return true;
}

bool getDate(const char *str)
{
  char Month[12];
  int Day, Year;
  uint8_t monthIndex;

  if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
  for (monthIndex = 0; monthIndex < 12; monthIndex++) {
    if (strcmp(Month, monthName[monthIndex]) == 0) break;
  }
  if (monthIndex >= 12) return false;
  tm.Day = Day;
  tm.Month = monthIndex + 1;
  tm.Year = CalendarYrToTm(Year);
  return true;
}

Now if you open your serial console, you should see that it has set the time correctly.

Finally you can upload the following code (after installing the libraries linked above) to get your clock up and running.

MAX7219_LED_Clock.ino

/*   FILENAME: MAX7219_LED_Clock.ino 
 *   AUTHOR(s): Jessica at AccessiblePixel.com
 *   CONTACT: media@accessiblepixel.com
 *   LICENSE: 
 *     
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//
// Include Libraries
//

#include <Time.h>  // Time Manipulation Library
#include <DS1307RTC.h>  // DS1307 RTC Library
#include "LedControl.h" // MAX72XX Library

//
// Defines
//

#define TOTAL_STRIPS 2 // Set How Many Strips of MAX7219 7 Segment Displays
#define DATAIN 9 // pin 9 is connected to the DataIn 
#define CLK 8 // pin 8 is connected to the CLK
#define LOAD 7 // pin 7 is connected to LOAD 

//
// Start LedControl MAX7219 Library
//

LedControl lc=LedControl(DATAIN,CLK,LOAD,TOTAL_STRIPS);

//
// Set Variables
//

int year4digit;  // 4 digit year

//
// Setup (Run Once)
//

void setup() {

//
// The MAX7219 needs waking up as it is in power-saving mode on startup.
// This will wake all connected displays.
//
  
  for (int loop = 0; loop < (TOTAL_STRIPS); loop++) {
    lc.shutdown(loop,false);
    // Set the brightness to a medium value
    lc.setIntensity(loop,4);
    // Clear the display
    lc.clearDisplay(loop);
   }
 }

//
// Loop (Run Forever)
//

void loop() {

//
// Get data from the DS1307 RTC
//

  tmElements_t tm;
    if (RTC.read(tm)) {
      year4digit = tm.Year + 1970;  // Create an accurate 4 digit year variable
    }
  
//
// Get Single Digits from each number using modulus
//
  
// Quick Guide to Modulus with Arduino
// Given the number 2018:
// How to get:
// 8 is 2018 % 10
// 1 is 2018 / 10 % 10
// 0 is 2018 / 100 % 10
// 2 is 2018 / 1000 % 10

  int firsthourdigit = tm.Hour /10 % 10;
  int secondhourdigit = tm.Hour % 10;
  int firstminutedigit = tm.Minute /10 % 10;
  int secondminutedigit = tm.Minute % 10;
  int firstseconddigit = tm.Second /10 % 10;
  int secondseconddigit = tm.Second % 10;
  int firstdigitday = tm.Day /10 % 10;
  int seconddigitday = tm.Day %10;
  int firstdigitmonth = tm.Month /10 % 10;
  int seconddigitmonth = tm.Month % 10;
  int firstdigityear = year4digit / 1000 % 10;
  int seconddigityear = year4digit / 100 % 10;
  int thirddigityear = year4digit / 10 % 10;
  int fourthdigityear = year4digit %10;

//
// Output the digits
//
    
//
// First Display (as wired) displaying Time: HH.MM.SS format
//
  
  lc.setDigit(0,6,firsthourdigit,false);    // First Hour Digit     2
  lc.setDigit(0,5,secondhourdigit,true);    // Second Hour Digit    1 (with a .)
  lc.setDigit(0,4,firstminutedigit,false);  // First Minute Digit   2
  lc.setDigit(0,3,secondminutedigit,true);  // Second Minute Digit  3 (with a .)
  lc.setDigit(0,2,firstseconddigit,false);  // First Second Digit   3
  lc.setDigit(0,1,secondseconddigit,true);  // Second Second Digit  4 (with a .)

//
// Second Display (as wired) displaying Date: DD.MM.YYYY format.
//
  
  lc.setDigit(1,7,firstdigitday,false);     // First Day Digit      1
  lc.setDigit(1,6,seconddigitday,true);     // Second Day Digit     8 (with a .)
  lc.setDigit(1,5,firstdigitmonth,false);   // First Month Digit    1
  lc.setDigit(1,4,seconddigitmonth,true);   // Second Month Digit   2 (with a .)
  lc.setDigit(1,3,firstdigityear,false);    // First Year Digit     2
  lc.setDigit(1,2,seconddigityear,false);   // Second Year Digit    0
  lc.setDigit(1,1,thirddigityear,false);    // Third Year Digit     1
  lc.setDigit(1,0,fourthdigityear,false);   // Fourth Year Digit    8

  delay(250);
}

ATTiny85 Art Piece and first circuit development

This is the first circuit designed with a stand alone chip that runs the basic blink sketch. It uses an ATTiny85.
To setup the Arduino IDE, there is an excellent guide available at highlowtech.org which also works with the latest version of the Arduino IDE.

ATTiny85 in a small wooden, black frame and circuit background.

Once you have setup a circuit to program the ATTiny85, you can choose how fast the processor runs. Usually you’d want to run it at 8MHz. However, because I am running the piece off a CR2032 lithium cell, power is at a minimum. I read the data-sheet for the ATTiny85 and found that at 8MHz (at 3volt) it uses around 3.5mA and at 1MHz it uses about 0.5mA. So using this I managed to save 3mA of power. The other issue was the LED. This was consuming approximately 15-20mA every time it lit up. So I added a 47k ohm resistor to it, and reduced its power consumption.

As the average CR2032 cell has 235mA capacity, with the original power consumption the piece would have only lasted about a day. With these power optimisations, it will last at least 17 days before the cell needs replacing.

The first revision of the circuit board, before power optimisation

The circuit has been placed on a small green breadboard, a CR2032 cell holder, a CR2032 cell, connecting wire, a 47K ohm resistor and the ATTiny85 chip. I also printed a circuit background for the piece.

The code the ATTiny85 is running is simple, it’s just a modified version of the default blink sketch.

void setup() {
  pinMode(0, OUTPUT);
}
void loop() {
  digitalWrite(0, HIGH);
  delay(1000);
  digitalWrite(0, LOW);
  delay(1000);
}