VFD Clock project using ESP32-S3 , directly manipulating the ESP registers to enable easier multiplexing and segment mapping.
The NOS IV22 - VFD tubes have a date code of 1986 so they were treated to some NOS Bakelight sockets, yes they were dis-com-boob-erated and the oxides polished off the pins.
Resin 3D facia board secured the sockets ready for soldering
Colour coded test Wiring loom
The IV22 tube has a "Hot" cathode filament, 8 Anode segments (inc dp) plus an Anode Gate.
Luckily VFD's are relative low voltage, however this iteration uses overkill MPS A42 transistors for switching 28Volts to the segments and multiplexed Anode gates (2N2222 - 2N3904 transistors could also be used). A simple 5V>28V boost module is used for the switching Anode supply.
Cathode current is happy @90mA (recommended 100mA).
To keep time after power loss a simple DS3231 RTC module with battery back is used.

---------------------------------------------------------------------------------------
Get you going Clock Code :-
Worthy of note :- a batch of the ESP32-S3 GPIO's run sequentially 1 - 18 so you can shout a loud Word of binary data to multiple GPIO's in a single command. (Kudus on you Expressif)
---------------------------------------------------------------------------------------
#include <DS3231.h> // RTC Real Time Clock
#include <Wire.h>
DS3231 myRTC;
bool century = false;
bool h12Flag; bool pmFlag;
byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits;
bool alarmDy, alarmH12Flag, alarmPmFlag;
int houR;int minutE;int seconD;
int numArray[12]={0x81,0xCF,0x92,0x86,0xCC,0xA4,0xE0,0x8F,0x80,0x8C,0x7F}; //8xSegments
int digitArray[7]={0xFE00,0xFD00,0xFB00,0xF700,0xEF00,0xDF00}; // 6x Display Anodes
#define PARALLEL_0 1 // start pin number number with consecutive GPIOs ESP32S2 can support 1-14 (workio) maybe 15-18
int countTime =0;
int count=0;
int counter=999;
void setup() {
Serial.begin(115200);
Wire.begin(42,41); // i2c pins on ESP32-S3
timeGet(); // call Debug to check if RTC is up and running
for (int i = 0; i < 14; i++) { pinMode(PARALLEL_0 + i, INPUT); } parallel_set_outputs();
} // set up the parallel registers
void loop() {
if (millis()-countTime >=1000){seconD=myRTC.getSecond();minutE=myRTC.getMinute();houR=myRTC.getHour(h12Flag, pmFlag);countTime=millis();} // every second call RTC clock
parallel_write((digitArray[0]) | (numArray[houR/10]));delay(1); //write to Registers
parallel_write((digitArray[1]) | (numArray[houR%10]));delay(1); // i.e Anode Gate and
parallel_write((digitArray[2]) | (numArray[minutE/10]));delay(1); // anode segments
parallel_write((digitArray[3]) | (numArray[minutE%10]));delay(1); // with segment maps
parallel_write((digitArray[4]) | (numArray[seconD/10]));delay(1); // delay affects multiplexing
parallel_write((digitArray[5]) | (numArray[seconD%10]));delay(1);
}
void parallel_write(uint16_t value) {
uint16_t output = (REG_READ(GPIO_OUT_REG) & ~(0xFFFF << PARALLEL_0)) | (((uint16_t)value) << PARALLEL_0);
REG_WRITE(GPIO_OUT_REG, output);
}
void parallel_set_outputs(void) {
REG_WRITE(GPIO_ENABLE_W1TS_REG, 0xFFFF << PARALLEL_0);
}
uint8_t parallel_read(void) { uint16_t input = REG_READ(GPIO_IN_REG); return (input >> PARALLEL_0);
}
void timeGet() {
Serial.print(myRTC.getYear(), DEC); Serial.print(' ');
Serial.print(myRTC.getMonth(century), DEC); Serial.print(" ");
Serial.print(myRTC.getDate(), DEC); Serial.print(" ");
Serial.print(myRTC.getDoW(), DEC); Serial.print(" ");
Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); Serial.print(" ");
Serial.print(myRTC.getMinute(), DEC); Serial.print(" ");
Serial.print(myRTC.getSecond(), DEC);
Serial.print(" T="); Serial.println(myRTC.getTemperature(), 2);
}
void setTime(){ // one time set clock up call
myRTC.setClockMode(false); // set to 24h //setClockMode(true); // set to 12h
myRTC.setYear(23);
myRTC.setMonth(8);
myRTC.setDate(6);
myRTC.setDoW(0);
myRTC.setHour(11);
myRTC.setMinute(45);
myRTC.setSecond(00);
}