Introduction
A few days ago, I heard one of my friends say that he was going to move to a new house. So, I thought about giving him some gifts. I just got a new version of the Firebeetle ESP32 controller from DFRobot. The size of the new version is smaller than the old one, and it uses a type-c port.
Then, I decided to make a network clock. This production will be very simple, getting the time from the internet via the wifi of the ESP32, and then you need to find a screen that displays the time. There are many options for the screen, LCD1602 or LED monochrome dot matrix for splicing. I accidentally saw a VFD fluorescent screen before, it was green-blue when it was lit, and it glowed with white light, which makes the whole screen a mottled beauty. So, I am going to use it here.
Build the shell model.
Use 3D printing to print out the shell. The cover on the back of the shell uses a 2mm acrylic sheet.
Fix the VFD and ESP inside the shell with hot melt glue.
Apply a little glue and close the lid.
Burn Program
Download two library files here first.
Time Library: https://github.com/PaulStoffregen/Time
Timezone Library: https://github.com/JChristensen/Timezone
Then change the wifi and password in setup () in the code at the end of the article to your own, and click burn.
#include <Arduino.h>
#include <HardwareSerial.h>
#include<stdlib.h>
#include <TimeLib.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiUdp.h>
#ifdef ARDUINO_AVR_UNO
SoftwareSerial Serial1( 2, 5 ); //RX, TX
#endif
static const char ntpServerName[] = "ntp.aliyun.com";
const int timeZone = 8;
WiFiUDP Udp;
unsigned int localPort = 8888;
time_t getNtpTime();
WiFiMulti WiFiMulti;
uint8_t din = 25; // DA
uint8_t clk = 26; // CK
uint8_t cs = 14; // CS
uint8_t Reset = 13; // RS
uint8_t en = 21; // EN
void write_6302(unsigned char w_data)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
digitalWrite(clk, LOW);
delayMicroseconds(5);
if ( (w_data & 0x01) == 0x01)
{
digitalWrite(din, HIGH);
delay(1);
}
else
{
digitalWrite(din, LOW);
delay(1);
}
w_data >>= 1;
digitalWrite(clk, HIGH);
delayMicroseconds(5);
}
}
void VFD_cmd(unsigned char command)
{
digitalWrite(cs, LOW);
delayMicroseconds(5);
write_6302(command);
digitalWrite(cs, HIGH);
delayMicroseconds(5);
}
void S1201_show(void)
{
digitalWrite(cs, LOW);//开始传输
delayMicroseconds(5);
write_6302(0xe8); //地址寄存器起始位置
digitalWrite(cs, HIGH); //停止传输
delayMicroseconds(5);
}
void VFD_init()
{
//SET HOW MANY digtal numbers
digitalWrite(cs, LOW);
delayMicroseconds(5);
write_6302(0xe0);
delayMicroseconds(5);
write_6302(0x07);//8 digtal
digitalWrite(cs, HIGH);
delayMicroseconds(5);
//set bright
digitalWrite(cs, LOW);
delayMicroseconds(5);
write_6302(0xe4);
delayMicroseconds(5);
write_6302(0xff);//leve 255 max
digitalWrite(cs, HIGH);
delayMicroseconds(5);
}
/******************************
在指定位置打印一个字符(用户自定义,所有CG-ROM中的)
x:0~11;chr:要显示的字符编码
*******************************/
void S1201_WriteOneChar(unsigned char x, unsigned char chr)
{
digitalWrite(cs, LOW); //开始传输
delayMicroseconds(5);
write_6302(0x20 + x); //地址寄存器起始位置
delayMicroseconds(5);
write_6302(chr + 0x30);
digitalWrite(cs, HIGH); //停止传输
delayMicroseconds(5);
S1201_show();
}
/******************************
在指定位置打印字符串
(仅适用于英文,标点,数字)
x:0~11;str:要显示的字符串
*******************************/
void S1201_WriteStr(unsigned char x, char *str)
{
digitalWrite(cs, LOW); //开始传输
delayMicroseconds(5);
write_6302(0x20 + x); //地址寄存器起始位置
delayMicroseconds(5);
while (*str)
{
write_6302(*str); //ascii与对应字符表转换
str++;
}
digitalWrite(cs, HIGH); //停止传输
delayMicroseconds(5);
S1201_show();
}
void setup() {
Serial.begin( 115200 );
delay(10);
pinMode(en, OUTPUT);
pinMode(clk, OUTPUT);
pinMode(din, OUTPUT);
pinMode(cs, OUTPUT);
pinMode(Reset, OUTPUT);
digitalWrite(en, HIGH);
digitalWrite(Reset, LOW);
delayMicroseconds(5);
digitalWrite(Reset, HIGH);
VFD_init();
WiFiMulti.addAP("wifi", "password");
while(WiFiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
S1201_WriteStr(0, "wifi...");
delay(2000);
Serial.print( "IP number assigned by DHCP is " );
Serial.println( WiFi.localIP() );
Serial.println( "Starting UDP" );
Udp.begin( localPort );
Serial.print( "Local port: " );
//Serial.println( Udp.localPort() );
Serial.println( "waiting for sync" );
setSyncProvider( getNtpTime );
setSyncInterval( 300 );
}
time_t prevDisplay = 0;
void loop() {
if( timeStatus() != timeNotSet ) {
if( now() != prevDisplay ) {
prevDisplay = now();
showtime();
}
}
}
void showtime() {
int Hour = hour();
int Min = minute();
int Sec = second();
int HourHigh,HourLow,MinHigh,MinLow,SecHigh,SecLow;
Serial.print(Hour);
Serial.print(":");
Serial.print(Min);
Serial.print(":");
Serial.println(Sec);
HourHigh=Hour/10;
if(HourHigh){
S1201_WriteOneChar(0, HourHigh);
}
else{
S1201_WriteOneChar(0, ' '-0x30);
}
HourLow=Hour%10;
S1201_WriteOneChar(1, HourLow);
S1201_WriteOneChar(2, ':'-0x30);
MinHigh=Min/10;
S1201_WriteOneChar(3, MinHigh);
MinLow=Min%10;
S1201_WriteOneChar(4, MinLow);
S1201_WriteOneChar(5, ':'-0x30);
SecHigh=Sec/10;
S1201_WriteOneChar(6, SecHigh);
SecLow=Sec%10;
S1201_WriteOneChar(7, SecLow);
}
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while( Udp.parsePacket() > 0 ) ; // discard any previously received packets
Serial.println( "Transmit NTP Request" );
// get a random server from the pool
WiFi.hostByName( ntpServerName, ntpServerIP );
Serial.print( ntpServerName );
Serial.print( ": " );
Serial.println( ntpServerIP );
sendNTPpacket( ntpServerIP );
uint32_t beginWait = millis();
while( millis() - beginWait < 1500 ) {
int size = Udp.parsePacket();
if( size >= NTP_PACKET_SIZE ) {
Serial.println( "Receive NTP Response" );
Udp.read( packetBuffer, NTP_PACKET_SIZE ); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = ( unsigned long )packetBuffer[40] << 24;
secsSince1900 |= ( unsigned long )packetBuffer[41] << 16;
secsSince1900 |= ( unsigned long )packetBuffer[42] << 8;
secsSince1900 |= ( unsigned long )packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println( "No NTP Response :-(" );
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket( IPAddress &address )
{
// set all bytes in the buffer to 0
memset( packetBuffer, 0, NTP_PACKET_SIZE );
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket( address, 123 ); //NTP requests are to port 123
Udp.write( packetBuffer, NTP_PACKET_SIZE );
Udp.endPacket();
}
Done. Personally, I love the display effect of this VFD very much. If you use a brown acrylic for the faceplate, it will have a display effect close to white. But I like the beauty of being able to see the internal structure, which looks complicated but organized.