Elite Analog Clock project with ESP32, RTC and LED Matrix

ESP32 is a very versatile microcontroller with lots of computing power for lots of application. Here’s a fun project using RTC and LED matrix to build a hi-tech analog clock.

About the circuit

If you are not yet familiar with RTC module, it is used basically for time keeping.

LED MATRIX and ESP32 connection:
#define R1_PIN 19
#define G1_PIN 13
#define B1_PIN 18
#define R2_PIN 5
#define G2_PIN 12
#define B2_PIN 17

#define A_PIN 16
#define B_PIN 14
#define C_PIN 4
#define D_PIN 27
#define E_PIN 25 //–> required for 1/32 scan panels, like 64x64px. Any available pin would do, i.e. IO32.

#define LAT_PIN 26
#define OE_PIN 15
#define CLK_PIN 2

Here is the pinout of the HUB75E LED Matrix module.

Just follow along. I do not have time to draw the schematic for now. πŸ™‚

For the RTC module, it is simply connected to I2c line and 3.3V power. The RTC module have built-in battery so it can still track time even when the system has no power.

I have not time yet to put these schematic together. If I do, I will update this page so you can visit it on later date.

Arduino Code

Here’s the code that I have written. It is not yet that clean but at least it works. πŸ™‚

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>  tataylino.com  <<<<<<<<<<<<<<<<<<<<<<<<<
// Dot matrix clock using RTC module, ESP32, and LED Matrix module
// by: tataylino.com
// Reference:
// https://randomnerdtutorials.com/esp32-ds3231-real-time-clock-arduino/
// https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA


//------  Including the libraries ----------
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#include "time.h"
#include "RTClib.h"

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

//----------------------------------------Defines the connected PIN between P5 and ESP32.
#define R1_PIN 19
#define G1_PIN 13
#define B1_PIN 18
#define R2_PIN 5
#define G2_PIN 12
#define B2_PIN 17

#define A_PIN 16
#define B_PIN 14
#define C_PIN 4
#define D_PIN 27
#define E_PIN 25  //--> required for 1/32 scan panels, like 64x64px. Any available pin would do, i.e. IO32.

#define LAT_PIN 26
#define OE_PIN 15
#define CLK_PIN 2
//----------------------------------------

//----------------------------------------Defines the P5 Panel configuration.
#define PANEL_RES_X 64  //--> Number of pixels wide of each INDIVIDUAL panel module. 
#define PANEL_RES_Y 64  //--> Number of pixels tall of each INDIVIDUAL panel module.
#define PANEL_CHAIN 1   //--> Total number of panels chained one to another
//----------------------------------------

// Initialize MatrixPanel_I2S_DMA as "dma_display".
MatrixPanel_I2S_DMA *dma_display = nullptr;

//----------------------------------------Variable for color.
// color565(0, 0, 0); --> RGB color code. Use the "color picker" to use or find another color code.
uint16_t myBLACK = dma_display->color565(0, 0, 0);
uint16_t myWHITE = dma_display->color565(255, 255, 255);
uint16_t myRED = dma_display->color565(255, 0, 0);
uint16_t myGREEN = dma_display->color565(0, 255, 0);
uint16_t myBLUE = dma_display->color565(0, 0, 255);
uint16_t clock_bg = dma_display->color565(10, 10, 0);

// *************  Clock Colors ************
// value is from 0 to 15 only
// Second hand
uint8_t s_hand_r = 12;
uint8_t s_hand_g = 1;
uint8_t s_hand_b = 1;
// Minute Hand
uint8_t m_hand_r = 1;
uint8_t m_hand_g = 1;
uint8_t m_hand_b = 12;
// hour hand
uint8_t h_hand_r = 1;
uint8_t h_hand_g = 12;
uint8_t h_hand_b = 1;
// numbers
uint8_t num_r = 1; 
uint8_t num_g = 10;
uint8_t num_b = 10;
// dots
uint8_t dot_r = 10;
uint8_t dot_g = 10;
uint8_t dot_b = 1;
// Screen Brightness
uint8_t led_brightness = 50; // 0 to 255

//----------------------------------------
#define clear()          matrix.fillScreen(0)
#define show()           matrix.swapBuffers(false)
#define WIDTH 64
#define HEIGHT 64
const byte CENTRE_X = 32;
const byte CENTRE_Y = 32;

uint8_t second;
uint8_t second_h;

uint8_t minute =1;
uint8_t minute_h =1;
uint8_t hour;
uint8_t hour_h;


void drawclockNB() {
  // Draw clock Numbers
  dma_display->setTextColor(dma_display->color444(num_r, num_g, num_b));
  dma_display->setTextSize(1); 
  dma_display->setCursor(26, 4);
  dma_display->println("12");
  dma_display->setCursor(43, 8);
  dma_display->println("1");
  dma_display->setCursor(51, 15);
  dma_display->println("2");
  dma_display->setCursor(56, 29);
  dma_display->println("3");
  dma_display->setCursor(51, 40);
  dma_display->println("4");
  dma_display->setCursor(43, 49);
  dma_display->println("5");
  dma_display->setCursor(30, 54);
  dma_display->println("6");
  dma_display->setCursor(17, 49);
  dma_display->println("7");
  dma_display->setCursor(8, 40);
  dma_display->println("8");
  dma_display->setCursor(4, 29);
  dma_display->println("9");
  dma_display->setCursor(7, 17);
  dma_display->println("10");
  dma_display->setCursor(14, 8);
  dma_display->println("11");
}

void drawclockDot() {
  // Draw clock dots
      float radians, angle;
      for (int i = 0; i < 60; i+=5) {
        //uint16_t color = MyColor[(co0+i*5)%92];
        angle =  360 - i * 6;
        radians = radians(angle);
        int x0 = CENTRE_X + 30 * sin(radians);
        int y0 = CENTRE_Y + 30 * cos(radians);
        //matrix.fillCircle(x0, y0, 1, color);
        dma_display->drawCircle(x0, y0, 1, dma_display->color444(dot_r, dot_g, dot_b));
     }
}

void draw_hand(uint8_t val, uint8_t lenght, uint8_t r, uint8_t g, uint8_t b ){
  float radians, angle;
  val = val + 30; // offset
  angle =  360 - val * 6;
  radians = radians(angle);
  int x0 = CENTRE_X + lenght * sin(radians);
  int y0 = CENTRE_Y + lenght * cos(radians);
  dma_display->drawLine(CENTRE_X, CENTRE_Y, x0, y0, dma_display->color444(r, g, b));
}


void update_time(void){
  dma_display->clearScreen();
  dma_display->fillScreen(clock_bg);
  drawclockNB();
  drawclockDot();
  //draw_minute(minute);
  draw_hand(second_h, 22, s_hand_r, s_hand_g, s_hand_b);  // Second Hand
  draw_hand(minute_h, 18, m_hand_r, m_hand_g, m_hand_b);  // Minute Hand
  draw_hand(hour_h,   12, h_hand_r, h_hand_g, h_hand_b);  // Hour Hand
}

//________________________________________________________________________________VOID SETUP()
void setup() {
  Serial.begin(115200);
  Serial.println("tataylino.com - Starting clock");
  delay(1000);

  // Initialize the connected PIN between Panel P5 and ESP32.
  HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
  delay(10);

  //----------------------------------------Module configuration.
  HUB75_I2S_CFG mxconfig(
    PANEL_RES_X,   //--> module width.
    PANEL_RES_Y,   //--> module height.
    PANEL_CHAIN,   //--> Chain length.
    _pins          //--> pin mapping.
  );
  delay(10);
  //----------------------------------------

  // Set I2S clock speed.
  mxconfig.i2sspeed = HUB75_I2S_CFG::HZ_10M;  // I2S clock speed, better leave as-is unless you want to experiment.
  delay(10);

  //----------------------------------------Display Setup.
  dma_display = new MatrixPanel_I2S_DMA(mxconfig);
  dma_display->begin();
  dma_display->setBrightness8(led_brightness); //--> 0-255.
  //----------------------------------------
  
  dma_display->clearScreen();
  dma_display->fillScreen(myGREEN);
  delay(500);
  dma_display->fillScreen(myBLUE);
  delay(500);
  
  dma_display->clearScreen();
  delay(100);

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

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, 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(2025, 6, 22, 20, 20, 0));

  Serial.println("Initialization done!");


}
//________________________________________________________________________________

//________________________________________________________________________________VOID LOOP()
void loop() {

  //unsigned long currentMillis = millis();

    // Get the current time from the RTC
  DateTime now = rtc.now();
  
  // Getting each time field in individual variables
  // And adding a leading zero when needed;
  String yearStr = String(now.year(), DEC);
  String monthStr = (now.month() < 10 ? "0" : "") + String(now.month(), DEC);
  String dayStr = (now.day() < 10 ? "0" : "") + String(now.day(), DEC);
  String hourStr = (now.hour() < 10 ? "0" : "") + String(now.hour(), DEC); 
  String minuteStr = (now.minute() < 10 ? "0" : "") + String(now.minute(), DEC);
  String secondStr = (now.second() < 10 ? "0" : "") + String(now.second(), DEC);
  String dayOfWeek = daysOfTheWeek[now.dayOfTheWeek()];

  // Complete time string
  String formattedTime = dayOfWeek + ", " + yearStr + "-" + monthStr + "-" + dayStr + " " + hourStr + ":" + minuteStr + ":" + secondStr;

  // Print the complete formatted time
  //Serial.println(formattedTime);
  //Serial.println(now.hour());
  //Serial.println(now.minute());
  //Serial.println(now.second());

  // initial settings
  second = now.second();
  minute = now.minute();
  hour = now.hour();

if(hour>12) hour = hour - 12;

  //compute hands position
  second_h = second;
  minute_h = minute;
  hour_h = (hour * 5) + (minute/12);

  update_time();
  dma_display->drawCircle(CENTRE_X, CENTRE_Y, 33, dma_display->color444(10, 0, 0));
  dma_display->drawCircle(CENTRE_X, CENTRE_Y, 1, dma_display->color444(10, 0, 0));

  // Getting temperature
  //Serial.print(rtc.getTemperature());
  //Serial.println("ΒΊC");

  //Serial.println();
  delay(1000);

}
//________________________________________________________________________________
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Take note that you will need to install the RTC and ESP32-HUB75-MatrixPanel-I2S-DMA libraries to make it work.

Youtube Video: