1.7 TFT Digital Clock

Display a ticking digital clock on the TFT LCD screen with the TFT_eSPI library.

Overview

This example brings the onboard TFT LCD to life by rendering a watch-style digital clock. It draws a static bezel background once at startup, then updates the time display every second using a software clock.

What You’ll Learn

  • Initializing the TFT display with TFT_eSPI

  • Drawing rectangles, rounded rectangles, and decorative lines

  • Using custom 7-segment fonts for digital clock displays

  • Simple software timekeeping with millis()

Before You Begin

Complete the setup steps in Preparation before continuing.

Note

Use the recommended Arduino IDE settings in Recommended Board Settings unless this lesson says otherwise.

Important

The TFT_eSPI library must be configured for this board’s display pins before this example will compile. A matching User_Setup.h is provided in the library ZIP.

Open the example sketch from the code package:

LAFVIN-ESP32-S3-Multimedia-Kit-main/1.Arduino/7.TFT_Watch/7.TFT_Watch.ino

This example uses the onboard TFT LCD. No external wiring is required.

Upload and Test

  1. Connect the board with a USB Type-C data cable.

  2. Open 7.TFT_Watch.ino in Arduino IDE.

  3. In the Tools menu, select ESP32S3 Dev Module as the board and choose the correct serial port.

  4. Click Upload.

  5. After uploading, the TFT screen shows a watch face with hours, minutes, and seconds. The time starts from the sketch’s compile time and advances every second.

The display layout:

  • White outer background

  • Dark grey rounded bezel frame

  • Light grey LCD face with decorative lines

  • HH:MM in large 7-segment font near the centre

  • SS in smaller size below

  • “LAFVIN” branding at the bottom

How the Code Works

Display Initialization

#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();
tft.init();
tft.setRotation(0);         // Portrait orientation
tft.invertDisplay(true);    // Correct colours for this panel

Drawing the Watch Face

The watch face is drawn once in setup() using filled rectangles, horizontal lines, and text labels. A dark bezel frame sits on a white background, with a lighter LCD face area inside.

tft.fillScreen(TFT_WHITE);
tft.fillRect(5, 40, 230, 220, WATCH_FRAME);
tft.fillRect(15, 50, 210, 200, LCD_BACK_LIGHT);

tft.drawFastHLine(20, 80, 200, TFT_BLUE);
tft.drawFastHLine(20, 220, 200, TFT_BLUE);

tft.setTextColor(LCD_TEXT_BLACK, LCD_BACK_LIGHT);
tft.setTextDatum(TL_DATUM);
tft.drawString("ALARM CHRONO", 20, 55, 2);
tft.setTextDatum(TR_DATUM);
tft.drawString("LIGHT", 220, 55, 2);

tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_DARKGREY, TFT_WHITE);
tft.drawString("LAFVIN", 120, 280, 4);

Software Clock

The initial time is parsed from the __TIME__ compile-time macro using a conv2d() helper that extracts two-digit numbers from the "HH:MM:SS" string. The loop() increments seconds, minutes, and hours with carry, then redraws only the changed digits using font 7 (a 7-segment display font).

static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6);

void loop() {
  if (targetTime < millis()) {
    targetTime += 1000;

    ss++;
    if (ss == 60) { ss = 0; mm++; }
    if (mm > 59)   { mm = 0; hh++; }
    if (hh > 23)   { hh = 0; }

    String mainTime = hourStr + ":" + minStr;
    tft.setTextDatum(MC_DATUM);
    tft.drawString(mainTime, 120, 130, 7);
    tft.drawString(secStr, 120, 190, 7);
  }
}

In the provided sketch:

  • The watch face is drawn once in setup() and not redrawn

  • Only the changing time digits are updated in loop()

  • A 7-segment font (font 7) gives the clock an authentic digital-watch look

  • setTextDatum(MC_DATUM) centres the text horizontally and vertically at the given coordinate

Try This Next

  • Change tft.setRotation(0) to tft.setRotation(1) for a landscape clock

  • Add the date below the time using a smaller font

  • Replace __TIME__ with an NTP-synced clock for real internet time (see 2.13 WiFi Connection Manager for a Wi-Fi example)