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.
Weld the ESP32 and screen first.
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.
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.
CODE
#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);//Start transmitting
delayMicroseconds(5);
write_6302(0xe8); //Initial position of address register
digitalWrite(cs, HIGH); //Stop transmitting
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);
}
/******************************
Print one char at the specified position(user-defined, all in CG-ROM)
x:0~11;chr: Char code to display
*******************************/
void S1201_WriteOneChar(unsigned char x, unsigned char chr)
{
digitalWrite(cs, LOW); //Start transmitting
delayMicroseconds(5);
write_6302(0x20 + x); //Initial position of address register
delayMicroseconds(5);
write_6302(chr + 0x30);
digitalWrite(cs, HIGH); //Stop tansmitting
delayMicroseconds(5);
S1201_show();
}
/******************************
Print string at the specified position
(Only suitable for English, punctuation, numbers)
x:0~11;str: string to display
*******************************/
void S1201_WriteStr(unsigned char x, char *str)
{
digitalWrite(cs, LOW); //Start transmitting
delayMicroseconds(5);
write_6302(0x20 + x); //Inital position of address register
delayMicroseconds(5);
while (*str)
{
write_6302(*str); //Conversion between ascii and corresponding char table
str++;
}
digitalWrite(cs, HIGH); //Stop transmitting
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 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.
Hope you like it, thanks for reading!
Comments