Maker.io main logo

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

2025-11-10 | By Mirko Pavleski

License: General Public License Arduino

These days, I accidentally came across this small retro black and white TV with a built-in radio, so I immediately decided to make a simple, useful device with it. Of course, first, I decided to check its current condition and functionality.

Let's examine the radio receiver. As far as I can see, this part of the device is functioning normally, and the only thing that needs to be cleaned is the volume potentiometer, possibly with some contact cleaner spray.

Now let's move on to the TV receiver. In a few seconds, a white raster appears on the screen, which is a sign that this part of the device is probably completely functional. We still have to examine the tuner part by bringing an analog signal to the antenna input. For this purpose, I will use the signal from cable television. This signal is in the VHF-III band, so now I will start searching for channels.

cvhg

As I assumed, the TV receiver is completely functional, but unfortunately, nowadays it has no significant use value except as part of some retro TV collection.

I got the idea to "bring back to life" this small, cute device in a way that I would make some kind of clock that would display the time and date on the screen. And of course, the most suitable for that purpose is the Arduino microcontroller together with the TVout library, with the help of which a composite signal is generated through only two resistors.

 

The model of this device is "Inno-Hit TV128," but after a long search, I was unable to find any service manual or circuit diagram. Many years ago, I worked in a TV repair shop, and I have relatively extensive knowledge in this area.

By definition, the composite input should be located somewhere between the video amplifier and the sync section. After a detailed examination of the PCB and its components, I discovered that the video amplifier in this case is a TA7678AP IC, and the sync section is a µPC1379C IC.

hjio

After several hours of experimentation, I discovered that the composite input is located right after the video output from the TA7678AP IC. That video signal passes through this jumper J112.

yuy

So I need to remove this jumper so that the previous tuner circuit does not affect the signal and connect the composite input directly to the input of the sync section.

The composite signal generator consists of an Arduino Nano and two resistors, and there is also the DS3231 real-time clock module with a built-in battery from which the exact time is read and displayed on the TV screen.

ghui

Now, for testing, I will send a demo signal, actually an example from the TVout library. One important note. If you install the TVout library by default via "manage libraries" or by copying it to the library folder, you will get the error "fontALL.h: No such file or directory" when compiling.

fhgf

To avoid this, we need to install the two libraries "TVout" and "TVoutfonts" separately, which are given at the end of the text.

Now let's start testing. We bring the composite signal generated by the Arduino to the appropriate input, which is connected in the manner explained previously. The switch should be in the "Composite" position. The content is clearly displayed on the screen.

kjp

Understandably, now the BAND switch has no function because the tuner section has been omitted. Now you can adjust the brightness and contrast of the image and, if necessary, horizontal or vertical synchronization. And as I mentioned earlier, I created a relatively simple code that will display the time, day of the week, and date in digital form. Initially, my idea was an analog clock, but due to the extremely low resolution that can be generated by the Arduino, the image was desperately bad.

jipj

The code is mainly based on the TVout library, so my goal was to introduce several different possibilities, such as drawing logos, rectangles, lines, and text borders, as well as black text on a white background.

The exact time and date are read from the DS3231 real-time clock module, which also has a built-in battery to save time when there is no power.

Otherwise, when modifying the TV, I installed a small switch that allows you to choose between standard TV mode and composite video mode.

gfju

And finally, a short conclusion. This project successfully transforms a vintage TV into a functional and stylish retro clock. It's a perfect fusion of classic hardware and modern microcontroller simplicity. Also, modifying the small TV without any documentation or schematics was a big challenge for me.

hj

Copy Code
// 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();
}
Mfr Part # A000005
ARDUINO NANO ATMEGA328 EVAL BRD
Arduino
22,20 €
View More Details
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.