Project tutorial

Tiny Calendar and Clock featuring Moon Phase in a LED Matrix © CC BY-NC-SA

This project introduces to you a tiny calendar using a LED Matrix and includes many features like the Moon phase!

• 4,138 views
• 1 comment
• 26 respects

Components and supplies

 Arduino UNO & Genuino UNO
×1
 Rotary Encoder with Push-Button
×1
 Real Time Clock (RTC)
×1
×1
 Maxim Integrated Max7219 LED Matrix
×1
 Jumper wires (generic)
×1

Apps and online services

 Arduino IDE

Hello, everybody!

After I did my project Binary Clock in a LED matrix, I started to develop the idea of a complete monthly calendar and also including features like digital clock, number of the week of the year and the Moon phases. The result is the Tiny Calendar where I have introduced a lot of features in a single LED matrix.

To be capable to show so much information in an 8 x 8 matrix, some characteristics were shown in binary notation like the numbers of the week, month and year.

A difficult challenge was about the way to show the complete calendar according to correct days of week for a certain year and the solution you will see is completely original for this kind of application.

Let's see it!

Bill of Material

• 1 x Arduino UNO R3
• 1 x LED Matrix Display 8x8 Module with MAX7219
• 1 x Rotary Encoder Module KY-040 with push button
• 1 x Real Time Clock Module RTC DS1307
• 1 x Kit of Jumpers Male-Male and Female-Female

Assembly

The assembly is very simple due to the use of modular components. Just follow the schematic and take care with the wires connections.

Print the corresponding template of the Hemisphere (Northern or Southern) where do you live, cut and put it over the LED matrix according with the position shown in the pictures.

Programming

As I mentioned at the beginning, the main challenge of this project was to find out a way to put all information on a single display 8x8 and also how to manage individually all the LEDs of the matrix.

To support me in this task, I did a model on Excel Worksheet simulating this operation with Visual Basic programming. The option to use the rotary encoder was due to its accuracy. I tried to use potentiometers, but the results were not satisfactory in this application.

For the timer, I decided to apply the TimerOne library to read the rotary encoder each 1 millisecond and the results were really good.

To manage the paging feature, I used the functions while() and millis() to control the loop of calendar during 30 seconds and then the display is cleaned to show the time of digital clock.

These are the libraries I have used with Arduino IDE:

• Time.h - Library with date and time functions
• LedControl.h - Library to control the LED Matrix
• Wire.h - Library to support the DS130RTC
• DS130RTC.h - Library of the Real Time Clock
• Bounce2.h - Library to read the button of rotary encoder
• TimerOne.h - Library of the Timer 1 to read the rotary encoder.

Related to the Moon phase calculus, I wanted an algorithm that was good enough for quotidian application without the scientific accuracy of a routine with long calculations.

Operation

The operation of Tiny Calendar is really simple and all the control is done using the rotary encoder. First of all, you need to select with the push button of rotary encoder what feature do you want to use like the day, month or year.

The feature that was selected is highlighted with a short blink of corresponding LEDs. To read the characteristics of Week, Month and Year, you need sum all numbers of each one that are with the LEDs turned on.

The Hour is shown in digital format and in the row of matrix (used to show the Year too) you can see the LEDs corresponding to 16 and 8 turned on, representing the 24 hours (16 + 8). For the Minutes, the LEDs corresponding to 32, 16, 8, 4 (sum is 60 minutes) in the last row of matrix are turned on indicating they are shown the minutes.

For the Calendar, the main reference is the column that is blinking. This column represents the Sunday (1st day of the week). The next column at right is Monday, then Tuesday and so on until Saturday.

Note: The Sunday column can move for any other column depending of month and year set in the calendar.

Custom parts and enclosures

Template for Northern Hemisphere due to Moon Phases.
Template for Southern Hemisphere due to Moon Phases.

Schematics

Schematics in PDF for Tiny Calendar

Code

Tiny_Calendar_V1.inoArduino
Arduino Code for Tiny Calendar
```/*      Tiny Calendar
V1.1 - 09.Dec.2016
by LAGSILVA
*/

#include <Time.h> //Biblioteca com as funes de tempo e calendrio
#include <LedControl.h> //Biblioteca para o controle de LED com o MAX72XX
#include <Wire.h> //Biblioteca auxiliar para o DS1307RTC - Pinos para Arduino UNO: A4 (SDA), A5 (SCL)
#include <DS1307RTC.h> //Biblioteca para o Real Time Clock
#include <Bounce2.h> //Biblioteca para ler botao do Encoder
#include <TimerOne.h> //Biblioteca para o Timer 1

//Declara variaveis globais
//Variaveis para o calendario
int dia, mes, ano, bis ;
int diames[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //Total de dias em cada mes
int codmes[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; //Total de dias acumulados no ano ate determinado mes
int codmesBis[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; //Total de dias acumulados no ano bissexto
//Variaveis para o encoder
byte encoderPinA = 7;  //Variavel do Encoder - CLK
byte encoderPinB = 8;  //Variavel do Encoder - DT
byte encoderPinSW = 9; //Variavel do Encoder - SW
byte encoderPos = 0;
byte encoderPinALast = LOW;
byte n = LOW;
byte linSW = 0;
unsigned long t2;  //Variavel para o "Timer" 2
Bounce SW = Bounce(); // Define o objeto Bounce para leitura do botao do Encoder

/*
Pin numbers do Arduino para conexao com o MAX72XX (matriz de LED com controlador MAX72XX)
pin 2 esta conectado no DataIn (DIN)
pin 4 esta conectado no CLK (CLK)
*/
LedControl lc = LedControl(2, 4, 3, 1);

//Matriz de Numeros - 0 a 9 - Tamanho 5x3
byte num[10][5] = {
{7, 5, 5, 5, 7},
{2, 6, 2, 2, 2},
{7, 1, 7, 4, 7},
{7, 1, 7, 1, 7},
{5, 5, 7, 1, 1},
{7, 4, 7, 1, 7},
{7, 4, 7, 5, 7},
{7, 1, 2, 2, 2},
{7, 5, 7, 5, 7},
{7, 5, 7, 1, 7},
};

void setup() {

pinMode (encoderPinA, INPUT);
pinMode (encoderPinB, INPUT);
pinMode (encoderPinSW, INPUT_PULLUP);

// Setup do Bounce:
SW.attach(encoderPinSW);
SW.interval(100);

setSyncProvider(RTC.get);   // Funcao para ler o tempo real do RTC (Real Time Clock)
dia = day();
mes = month();
ano = year();

Timer1.initialize(1000); // Ajusta o Timer 1 para 1000 microsegundos
Timer1.attachInterrupt(lerEncoder); //Timer para a leitura do Encoder

//Ativa o MAX72XX
lc.shutdown(0, false);

/* Ajusta o brilho do display entre 0 e 15 */
lc.setIntensity(0, 3);
/* Limpa o display */
lc.clearDisplay(0);

}

void loop() {

//Atualiza o Timer 2 para calculos e plotagem dos dados
t2 = millis();

while ((millis() - t2) < 30000) { //Executa o Timer de 30000 milisegundos

//Calculos

//Calculo do Ano Bissexto
bis = 0;
if (((ano % 4) == 0) && ((ano % 100) != 0) || ((ano % 2000) == 0)) {
bis = 1;
}

//Calculo do Codigo do Mes
int codMes = codmes[mes - 1];
if ((mes > 2) && (bis == 1)) {
codMes = codmesBis[mes - 1];
}

//Calculo da Fase da Lua
float m = mes + 1;
float y = ano;
if (mes < 3) {
m = mes + 12;
y = ano - 1;
}
long c = 365.25 * y;
long e = 30.6 * m;
float jd = (c + e + dia - 694039.09) / 29.53;
int faselua = int((jd - int(jd)) * 8 + 0.5) % 8;

//Calculo do dia da semana para o primeiro dia do mes selecionado
float tempocorrido = ((ano - 1970) * 365.25 + 0.25 + codMes) * 24 * 3600 + 7200;
int diasemana = weekday(tempocorrido);

//Calculo do numero da semana do ano para o dia selecionado
float isosem = ((ano - 1970) * 365.25 + 0.25 + codMes + dia - 1) * 24 * 3600;
isosem = weekday(isosem) - 1;
if (isosem <= 0) {
isosem = isosem + 7;
}
int numsem = int((dia + codMes - isosem + 10) / 7);

//Plotagens da Matriz de LED

//Plotagem Piscante da Fase da Lua na Coluna 0
lc.setColumn(0, 0, 128 >> (7 - faselua));
delay(300);
lc.setColumn(0, 0, 0);
delay(100);

//Plotagem do Calendario Mensal
for (k = 0; k < 4; k++) {
lc.setRow(0, k, B1111111); //Plota os 28 primeiros dias do calendario
}
for (k = 29; k <= diames[mes - 1] + bis * (mes == 2); k++) {
lc.setLed(0, 4, k % 7, true); //Plota os ultimos dias do mes
}
for (k = diames[mes - 1] + bis * (mes == 2) + 1; k <= 35; k++) {
lc.setLed(0, 4, k % 7, false); //Apaga os ultimos dias do mes anteriormente selecionado
}
if (mes == 2 && bis == 0) {
for (k = 29; k <= 31; k++) {
lc.setLed(0, 4, k % 7, false); //Apaga os dias entre 29 e 31 caso o mes seja Fevereiro no em ano bissexto
}
}

//Plotagem do Domingo piscante em Coluna
int domingo = (9 - diasemana) % 7;
if (domingo == 0) {
domingo = 7; //Verifica a posio no calendrio para o Domingo
}

if (mes == 2) {
for (k = 0; k < 4; k++) {
lc.setLed(0, k, domingo, true);
delay(300);
for (k = 0; k < 4; k++) {
lc.setLed(0, k, domingo, false);
}
delay(100);
}
}
else {
if (domingo < 4) {
for (k = 0; k < 5; k++) {
lc.setLed(0, k, domingo, true);
delay(300);
for (k = 0; k < 5; k++) {
lc.setLed(0, k, domingo, false);
}
delay(100);
}
}
else {
for (k = 0; k < 4; k++) {
lc.setLed(0, k, domingo, true);
delay(300);
for (k = 0; k < 4; k++) {
lc.setLed(0, k, domingo, false);
}
delay(100);
}
}
}

//Plotagem do dia selecionado em modo piscante
int lin = 0;
int col = 0;
col = dia % 7;
if (col == 0) {
col = 7;
}
lin = int(dia / 7);
if (col == 7) {
lin = lin - 1;
}
lc.setLed(0, lin, col, true);
delay(300);
lc.setLed(0, lin, col, false);
delay(100);

//Plotagem do nmero da semana do ano na Linha 5 para a data escolhida
lc.setRow(0, 5, numsem);

//Plotagem do Ms na Linha 6 em binrio
lc.setRow(0, 6, mes);

//Plotagem do Ano na Linha 7 em binrio
lc.setRow(0, 7, ano - 2000);

}

//Plotagem das Horas
lc.clearDisplay(0);
int hora1 = hour() / 10;
int hora2 = hour() % 10;
for (k = 0; k < 5; k++) {
byte temp = num[hora1][k];
temp = (temp << 4) + num[hora2][k];
lc.setRow(0, k, temp);
}
lc.setRow(0, 7, 24);
delay(2500);

//Plotagem dos Minutos
lc.clearDisplay(0);
int minuto1 = minute() / 10;
int minuto2 = minute() % 10;
for (k = 0; k < 5; k++) {
byte temp = num[minuto1][k];
temp = (temp << 4) + num[minuto2][k];
lc.setRow(0, k, temp);
}
lc.setRow(0, 7, 60);
delay(2500);
lc.clearDisplay(0);

}

void lerEncoder() {  //Leitura do Encoder

// Update the Bounce instance :
SW.update();
//Verificar estado do botao do Encoder
byte ledState = false;
if (SW.fell()) {
ledState = !ledState;
lc.setLed(0, linSW + 5, 1, ledState); //Indica no display qual o modo escolhido (Dia,Mes ou Ano)
linSW++;
if (linSW > 2) {
linSW = 0;
}
}

switch (linSW) {

case 0: //Botao na posicao para ajustar o ano
if ((encoderPinALast == LOW) && (n == HIGH)) {
encoderPos++;
ano++;
} else {
encoderPos--;
ano--;
}
encoderPinALast = n;
}

case 1: //Botao na posicao para ajustar o dia
if ((encoderPinALast == LOW) && (n == HIGH)) {
encoderPos++;
dia++;
} else {
encoderPos--;
dia--;
}
encoderPinALast = n;
}

case 2: //Botao na posicao para ajustar o mes
if ((encoderPinALast == LOW) && (n == HIGH)) {
encoderPos++;
mes++;
} else {
encoderPos--;
mes--;
}
encoderPinALast = n;
}

dia = constrain(dia, 1, diames[mes - 1] + bis * (mes == 2));
mes = constrain(mes, 1, 12);
ano = constrain(ano, 2000, 2063);
encoderPinALast = n;
}
}
```

• 13 projects
• 39 followers

December 9, 2016

Members who respect this project

and 18 others

See similar projects
you might like

• 277 views
• 8 respects

• 173 views
• 3 respects

Windows PC Lock/Unlock Using RFID

Project tutorial by Prasanth K S

• 6,933 views
• 30 respects

Makers (Cubecon) #1 Infrared Transmit & Infrared Receive

Project in progress by Alpha

• 162 views
• 4 respects

• 1,423 views