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,483 views
  • 1 comment
  • 27 respects

Components and supplies

Apps and online services

About this project

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
  • 1 x Breadboard

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

Northern Template - Tiny Calendar
Template for Northern Hemisphere due to Moon Phases.
Southern Template - Tiny Calendar
Template for Southern Hemisphere due to Moon Phases.

Schematics

Tiny Calendar - 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
byte k; //Variavel para contadores
//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 3 esta conectado no LOAD (CS)
 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
      n = digitalRead(encoderPinA);
      if ((encoderPinALast == LOW) && (n == HIGH)) {
        if (digitalRead(encoderPinB) == LOW) {
          encoderPos++;
          ano++;
        } else {
          encoderPos--;
          ano--;
        }
        encoderPinALast = n;
      }

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

    case 2: //Botao na posicao para ajustar o mes
      n = digitalRead(encoderPinA);
      if ((encoderPinALast == LOW) && (n == HIGH)) {
        if (digitalRead(encoderPinB) == LOW) {
          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;
  }
}

Comments

Similar projects you might like

Magic Lamp

by Nekhil ravi

  • 823 views
  • 3 comments
  • 8 respects

Arduino MKR GSM 1400 and DTMF

by Arduino_Genuino

  • 3,438 views
  • 0 comments
  • 10 respects

Love You Pillow

Project tutorial by Arduino

  • 1,861 views
  • 0 comments
  • 4 respects

Infrared Replicator

Project tutorial by Gustavo Gonnet

  • 3,596 views
  • 6 comments
  • 26 respects

Rural Hack Kit

by Officine Innesto

  • 3,866 views
  • 0 comments
  • 7 respects

A Model Lighthouse Using an Adafruit NeoPixel Ring

Project tutorial by Jeremy Lindsay

  • 329 views
  • 0 comments
  • 4 respects
Add projectSign up / Login