Mirko Pavleski
Published © GPL3+

How to Make a Digital Clock on a Vintage B&W TV

This project successfully transforms a vintage TV into a functional and stylish retro clock.

IntermediateFull instructions provided3 hours18
How to Make a Digital Clock on a Vintage B&W TV

Things used in this project

Hardware components

Old CRT B&W TV
×1
Arduino Nano R3
Arduino Nano R3
×1
DS3231M - ±5ppm, I2C Real-Time Clock
Maxim Integrated DS3231M - ±5ppm, I2C Real-Time Clock
×1
Resistor 1k ohm
Resistor 1k ohm
×1
Resistor 4.75k ohm
Resistor 4.75k ohm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic

...

Code

Code

C/C++
....
// Arduino CRT TV Clock by mircemk, September 2025

#include <TVout.h>
#include <fontALL.h>
#include "MyLogo.h"
#include <Wire.h>
#include <avr/pgmspace.h> // For PROGMEM

TVout TV;

#define DS3231_I2C_ADDRESS 0x68

long previousMillis = 0;
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
long interval = 1000; // Update every 1 second
byte lastDayOfWeek = 0; // To track changes in day of week
byte lastDayOfMonth = 0; // To track changes in date

// Array of day names in PROGMEM (1=Sunday, 2=Monday, ..., 7=Saturday)
const char day0[] PROGMEM = "Unknown";
const char day1[] PROGMEM = "Sunday";
const char day2[] PROGMEM = "Monday";
const char day3[] PROGMEM = "Tuesday";
const char day4[] PROGMEM = "Wednesday";
const char day5[] PROGMEM = "Thursday";
const char day6[] PROGMEM = "Friday";
const char day7[] PROGMEM = "Saturday";
const char *const dayNames[] PROGMEM = {day0, day1, day2, day3, day4, day5, day6, day7};

byte decToBcd(byte val) {
  return ((val/10*16) + (val%10));
}

byte bcdToDec(byte val) {
  return ((val/16*10) + (val%16));
}

void getDateDs3231() {
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  second = bcdToDec(Wire.read() & 0x7f);
  minute = bcdToDec(Wire.read());
  hour = bcdToDec(Wire.read() & 0x3f);
  dayOfWeek = bcdToDec(Wire.read());
  dayOfMonth = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year = bcdToDec(Wire.read());
}

void setup() {
  Wire.begin();
  getDateDs3231();
  TV.begin(PAL, 130, 96);
  TV.select_font(font8x8);
  TV.set_cursor(15, 13);
  TV.print("DIY Arduino");
  TV.set_cursor(22, 40);
  TV.print("CRT Clock");
  TV.draw_rect(20, 38, 75, 13, 1, -1);
  TV.draw_rect(17, 35, 81, 19, 1, -1);
  TV.set_cursor(8, 67);
  TV.print("TVout Library");
  TV.delay(5000);
  TV.clear_screen();
  intro();
  TV.set_cursor(12, 2);
  TV.print("Time & Date:");
  TV.draw_rect(6, 0, 105, 12, 1, 2);


  TV.draw_line(15, 39, 103, 39, 1);
  TV.draw_line(15, 61, 103, 61, 1);

  // Initial display of day and date
  
  TV.set_cursor(25, 45);
  char buffer[10];
  strcpy_P(buffer, (char *)pgm_read_word(&dayNames[dayOfWeek]));
  TV.print(buffer);
  TV.set_cursor(20, 68);
  TV.print(char(dayOfMonth/10 + 0x30));
  TV.print(char(dayOfMonth%10 + 0x30));
  TV.print("/");
  TV.print(char(month/10 + 0x30));
  TV.print(char(month%10 + 0x30));
  TV.print("/");
  TV.set_cursor(68, 68);
  TV.print("20");
  TV.print(char(year/10 + 0x30));
  TV.print(char(year%10 + 0x30));
  lastDayOfWeek = dayOfWeek;
  lastDayOfMonth = dayOfMonth;
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    getDateDs3231();
    #ifdef TVOUT_VBLANK
    TV.wait_for_vblank(); // Synchronize with vertical blanking if available
    #endif
    printTime();
  }
}

void printTime() {
  // Clear and print time (changes every second)
  TV.draw_rect(25, 25, 48, 8, 0, 1); // Clear time area (width for HH:MM:SS)
  TV.set_cursor(25, 25);
  TV.print(char(hour/10 + 0x30));
  TV.print(char(hour%10 + 0x30));
  TV.print(":");
  TV.print(char(minute/10 + 0x30));
  TV.print(char(minute%10 + 0x30));
  TV.print(":");
  TV.print(char(second/10 + 0x30));
  TV.print(char(second%10 + 0x30));

  // Print day of week only if it has changed
  if (dayOfWeek != lastDayOfWeek && dayOfWeek >= 1 && dayOfWeek <= 7) {
    TV.draw_rect(25, 45, 80, 8, 0, 1); // Clear day area (width for longest day name)
    TV.set_cursor(25, 45);
    char buffer[10];
    strcpy_P(buffer, (char *)pgm_read_word(&dayNames[dayOfWeek]));
    TV.print(buffer);
    lastDayOfWeek = dayOfWeek;
  }

  // Print date only if dayOfMonth has changed
  if (dayOfMonth != lastDayOfMonth) {
    TV.draw_rect(10, 68, 64, 8, 0, 1); // Clear date area (width for DD/MM/YYYY)
    TV.set_cursor(10, 68);
    TV.print(char(dayOfMonth/10 + 0x30));
    TV.print(char(dayOfMonth%10 + 0x30));
    TV.print("/");
    TV.print(char(month/10 + 0x30));
    TV.print(char(month%10 + 0x30));
    TV.print("/");
    TV.set_cursor(58, 68);
    TV.print("20");
    TV.print(char(year/10 + 0x30));
    TV.print(char(year%10 + 0x30));
    lastDayOfMonth = dayOfMonth;
  }
}

void intro() {
  unsigned char w, l, wb;
  int index;
  w = pgm_read_byte(MyLogo);
  l = pgm_read_byte(MyLogo+1);
  if (w & 7)
    wb = w/8 + 1;
  else
    wb = w/8;
  index = wb*(l-1) + 2;
  for (unsigned char i = 1; i < l; i++) {
    TV.bitmap((TV.hres() - w)/2, 0, MyLogo, index, w, i);
    index -= wb;
    TV.delay(50);
  }
  for (unsigned char i = 0; i < (TV.vres() - l); i++) {
    TV.bitmap((TV.hres() - w)/2, i, MyLogo);
    TV.delay(50);
  }
  TV.delay(3000);
  TV.clear_screen();
}

libraries

C/C++
..
No preview (download only).

Complete code

C/C++
..
No preview (download only).

Credits

Mirko Pavleski
198 projects • 1490 followers

Comments