Arduino Uno R4 LED Matrix Clock

by ArduinoPrints3D in Circuits > Gadgets

189 Views, 2 Favorites, 0 Comments

Arduino Uno R4 LED Matrix Clock

IMG_0569.JPG
I am a student and I designed this instructable. I hope you like it.😊

This is a project for a customisable LED clock based on the Arduino Uno R4. The 3d models are all designed in Autodesk Tinkercad.

Supplies

IMG_0529.JPG
IMG_0530.JPG
IMG_0531.JPG
IMG_0532.JPG
IMG_0533.JPG
IMG_0534.JPG
IMG_0535.JPG
  1. Arduino Uno R4
  2. 32 x 8 MAX7219 LED Matrix
  3. Rotary Encoder
  4. DS1307 Module
  5. Mini Solderless Breadboard
  6. Jumper Wires
  7. 10k Ω resistor
  8. 100 uF electrolydic capacitor
  9. 0.1 uF ceramic Capacitor

Test the Compnents.

circuit_image.png

I first tested each component. I wired them together as shown in the image above. I added resistors to help smooth out current spikes from the dot matix. I also added a pull down resistor to the pin 8 of the arduino to help the pin from floating on startup which was making the max7219 flash.

I used modified example codes to test everything. The first is for the max7219 module and should display "Hello! LED Matrix test, 1 2 3 4..." The second is for the rotary encoder. It should serial print whenever the buton is pressed or released and whenever the encoder is turned left or right. The last is for the RTC module. It should display the date and time, the unix time, and calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future.

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 8

byte counter = 0;
char counterChar[4] = {0};
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void setup()
{
P.begin();
P.setTextAlignment(PA_CENTER);
P.print("Hello!");
delay(4000);
P.displayText("LED Matrix test.", PA_CENTER, 50, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}

void loop()
{
if(P.displayAnimate()){
counter++;
sprintf(counterChar, "%d", counter);
P.displayText(counterChar, PA_CENTER, 20, 0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
}
}


#include <CtrlBtn.h>
#include <CtrlEnc.h>

// Define an onPress handler.
void onPress() {
Serial.println("Basic button pressed");
}

// Define an onRelease handler.
void onRelease() {
Serial.println("Basic button released");
}

// Define an onTurnleft handler.
void onTurnLeft() {
Serial.println("Basic rotary encoder turn left");
}

// Define an onTurnRight handler.
void onTurnRight() {
Serial.println("Basic rotary encoder turn right");
}
/*
Create a button with:
- signal pin.
- bounce duration.
- onPress handler (optional).
- onRelease handler (optional).
*/
CtrlBtn button(2, 15, onPress, onRelease);

// Create a rotary encoder with the clk signal pin number, dt signal pin number,
// onTurnleft & onTurnRight handler.
CtrlEnc encoder(4, 3, onTurnLeft, onTurnRight);

void setup() {
Serial.begin(115200);
}

void loop() {
// The process method will poll the button object and handle all it's functionality.
button.process();
encoder.process();
}


// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include "RTClib.h"

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
Serial.begin(57600);

#ifndef ESP8266
while (!Serial); // wait for serial port to connect. Needed for native USB
#endif

if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}

if (! rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop () {
DateTime now = rtc.now();

Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();

Serial.print(" since midnight 1/1/1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");

// calculate a date which is 7 days, 12 hours, 30 minutes, and 6 seconds into the future
DateTime future (now + TimeSpan(7,12,30,6));

Serial.print(" now + 7d + 12h + 30m + 6s: ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();

Serial.println();
delay(3000);
}

Since all modules were working I moved on to the next step.

Making the Clock Code

IMG_0575.JPG
IMG_0576.JPG
IMG_0577.JPG
IMG_0578.JPG
IMG_0579.JPG

Next i wrote the clock's code. It normally shows the time, which it gets off of the RTC on MAX7219. When you press the encoder you are brought to a menu where you can set the hours or minutes. Next you have the choice to either set the time you just chose on the clock or exit without setting the time.

////////////////////ARDUINO UNO R4////////////////////
////////////////////LED MATRIX CLOCK////////////////////

////////////////////LIBRARIES////////////////////

#include "RTClib.h"
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <CtrlBtn.h>
#include <CtrlEnc.h>
#include <BlockNot.h>

////////////////////VARIABLES////////////////////

static char displayChar[9] = {0}; //stores the text for the display to show
char status = 'N';
// status variable — indicates the clock's current mode.
//
// Legend:
// 'N' - Normal display (time view)
// 'h' - Enter hour-edit menu
// 'm' - Enter minute-edit menu
// 'H' - Hour edit screen
// 'M' - Minute edit screen
// 's' - Set/confirm time
// 'e' - Exit menu / return to normal
byte oldMin; //holds the last minute so screen updates when minute changes
int8_t setMinuteVar = 0; //holds the value when using the encoder to change the minute
int8_t setHourVar = 0; //holds the value when using the encoder to change the Hour

////////////////////PAROLA SETUP////////////////////

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 4
#define CS_PIN 8
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

////////////////////RTC SETUP////////////////////

RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

////////////////////BLOCKNOT SETUP////////////////////

BlockNot rtcRefreshTimer(1000);//check if time has changed once a second
BlockNot menuTimer(5000);//check if time has changed once a second

////////////////////CTRL SETUP AND MENU HANDLERS////////////////////
//Switch...Case is used to handle the status of the variables
// Define an onTurnleft handler.
void onTurnLeft() {
menuTimer.start(WITH_RESET);//resets the timer that automatically exits the menu
switch (status) {
case 'm':
status = 'h';
P.displayClear();
P.print("Hours");
break;
case 'H':
setHourVar--;
if(setHourVar <= -1){setHourVar = 23;}
snprintf(displayChar, sizeof(displayChar), "%02d", setHourVar);
P.displayClear();
P.print(displayChar);
break;
case 'M':
setMinuteVar--;
if(setMinuteVar <= -1){setMinuteVar = 59;}
snprintf(displayChar, sizeof(displayChar), "%02d", setMinuteVar);
P.displayClear();
P.print(displayChar);
break;
case 's':
status = 'm';
P.displayClear();
P.print("Minutes");
break;
case 'e':
status = 's';
P.displayClear();
P.print("Set");
break;
default:
break;
}
}

// Define an onTurnRight handler.
void onTurnRight() {
menuTimer.start(WITH_RESET);
switch (status) {
case 'h':
status = 'm';
P.displayClear();
P.print("Minutes");
break;
case 'm':
status = 's';
P.displayClear();
P.print("Set");
break;
case 'H':
setHourVar++;
if(setHourVar >= 24){setHourVar = 0;}
snprintf(displayChar, sizeof(displayChar), "%02d", setHourVar);
P.displayClear();
P.print(displayChar);
break;
case 'M':
setMinuteVar++;
if(setMinuteVar >= 60){setMinuteVar = 0;}
snprintf(displayChar, sizeof(displayChar), "%02d", setMinuteVar);
P.displayClear();
P.print(displayChar);
break;
case 's':
status = 'e';
P.displayClear();
P.print("Exit");
break;
default:
break;
}
}

// Define an onPress handler.
void onPress() {
menuTimer.start(WITH_RESET);
DateTime now = rtc.now();
switch (status) {
case 'N':
setHourVar = now.hour();
setMinuteVar = now.minute();
status = 'h';
P.displayClear();
P.print("Hours");
break;
case 'h':
status = 'H';
snprintf(displayChar, sizeof(displayChar), "%02d", setHourVar);
P.displayClear();
P.print(displayChar);
break;
case 'm':
status = 'M';
snprintf(displayChar, sizeof(displayChar), "%02d", setMinuteVar);
P.displayClear();
P.print(displayChar);
break;
case 'H':
status = 'h';
P.displayClear();
P.print("Hours");
break;
case 'M':
status = 'm';
P.displayClear();
P.print("Minutes");
break;
case 's':
status = 'N';
setTime(setHourVar, setMinuteVar);
showTime();
break;
case 'e':
status = 'N';
showTime();
break;
default:
break;
}
}

CtrlBtn button(2, 50, onPress);
CtrlEnc encoder(4, 3, onTurnLeft, onTurnRight);

////////////////////TIME SETTING FUNCTIONS////////////////////

void setTime(int8_t newHour, int8_t newMinute) {//take two variables and sets the time of the rtc to them
DateTime before = rtc.now();
DateTime updated(before.year(), before.month(), before.day(), newHour, newMinute, 0);
rtc.adjust(updated);
}

void showTime() {//updates the display and shows the current time.
DateTime now = rtc.now();
oldMin = now.minute();
if( status == 'N'){
snprintf(displayChar, sizeof(displayChar), "%02d:%02d", now.hour(), now.minute());
P.displayClear();
P.print(displayChar);
}
}

////////////////////SETUP CODE////////////////////

void setup () {
P.begin();
P.setIntensity(0);
if (! rtc.begin()) {
while (1) delay(10);
}
if (! rtc.isrunning()) {
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
DateTime now = rtc.now();
oldMin = now.minute();
P.setTextAlignment(PA_CENTER);
P.print("Hello!");//Turns the screen on and says hello
delay(4000);
showTime();
menuTimer.stop();
rtcRefreshTimer.start(WITH_RESET);
}

void loop () {
if (menuTimer.triggered()) {//This exits the menu automatically
switch (status) {
case 'h':
status = 'N';
showTime();
menuTimer.stop();
break;
case 'm':
status = 'N';
showTime();
menuTimer.stop();
break;
case 'H':
status = 'h';
P.displayClear();
P.print("Hours");
break;
case 'M':
status = 'm';
P.displayClear();
P.print("Minutes");
break;
case 's':
status = 'N';
showTime();
menuTimer.stop();
break;
case 'e':
status = 'N';
showTime();
menuTimer.stop();
break;
default:
break;
}

}

if(rtcRefreshTimer.triggered()){//checks if time has changed
DateTime now = rtc.now();
if (oldMin != now.minute()) {
showTime();
}
}
encoder.process();
button.process();
}

3D Modeling

Next I designed the 3d models with the help of my brother. We used Autodesk TinkerCAD which is probably the easiest and most enjoyable CAD software around. The models are based off of my Arduino Uno R4 LED Matrix project on NexPrint. We made it longer, taller, and added a mounting bracket for the rotary engoder. We also made a knob for the encoder and made the open portion of the base with thinner walls to accomidate the breadboard.

3D Printing

IMG_0536.JPG

Next I 3d printed the files attached in the previous step. I used orca slicer. The base and diffuser both had som peeling on the edges so make sure the bed is clean and a brim is applied.

Put Everything in Place

IMG_0538.JPG
IMG_0539.JPG
IMG_0540.JPG
IMG_0543.JPG
IMG_0544.JPG
IMG_0547.JPG
IMG_0549.JPG
IMG_0550.JPG

The first step of assembly is to put everything in its place.

  1. I slid the Arduino into it slot in the base.
  2. I hot glued the breadboard into the base near the Arduino. I had to cut som plastic tabs off of the breadboard to make it fit.
  3. I hot glued the RTC into the corner of the base.
  4. I screwed the MAX7219 onto the diffuser with the bracket.
  5. I screwed the rotary encoder onto the diffuser with the bracket.
  6. I pressed the knob onto the encoder. The encoder needed a bit of tape stuck onto its shaft to make it a friction fit.

Wire It All Up

circuit_image.png
IMG_0553.JPG
IMG_0551.JPG
IMG_0554.JPG
IMG_0556.JPG
IMG_0557.JPG
IMG_0560.JPG
IMG_0562.JPG

Follow the diagram above for instruction. I connected in these steps.

  1. I started by conecting the arduino to the breadboard's power rails.
  2. I added the capacitors.
  3. I connected all of the modules to the power rails.
  4. I connected the data lines for everything.

Screw the Lid On

IMG_0563.JPG
IMG_0564.JPG
IMG_0565.JPG

Insert the Diffuser into the base. Screw the lid on to fasten everything inside.

Done!

Arduino Uno R4 LED Matrix Clock

You should now have a fully functional clock!

If you want to see the clock in action watch this video!