A Project:

VFD -- IoT -- WiFi -- NTP -- WWW -- LUA -- ESP -- NodeMCU

November 2016


Discuss this Project on the Forums
or on the
Google+ article for this post

NodeMCU Clock

Introduction (Motivation):

As mentioned in previous articles (Such as this one about an Android-Based Entertainment Center), I have long been on a quest for something that should be simple.  All I want is a Clock that tells time. I want it to tell me the time; I don't want to tell it the time -- not even once. I certainly do not want to have it tell me the wrong time after a power failure, or twice a year when the time changes to/from Daylight Savings Time (DST).

It should also have basic functions like an alarm that can be set to a schedule that people actually have -- like waking up for work on weekdays, and sleeping in on Saturday and Sunday.

It should also have a usable user interface -- I don't want to wait 60 seconds for it to "go around again" if I hold a button an instant to long.

It should also have the "basics" like a readable display -- and it should look cool, too.

Mobile Phones, Tablets, PC's, and even some TV's (or Smart TV Boxes) get their time from the network. We don't "set" them, and they correctly follow DST. They also have convenient, sometimes clever, user interfaces for setting alarm times, etc.
Why shouldn't the clock on our bureau/mantle/bed table work the same way? 

So I set out to look for such a device. To make an already long introduction - long and not intolerable -- I didn't find any.

Yes, there are so-called "Atomic Clocks".  "Atomic Clocks" (in the sense of a consumer item for the bedroom or living room) -- aren't "Atomic" at all.  This term typically refers to clocks that attempt to receive a signal from WWV -- a radio station run by the National Institute of Standards. The problem is that there is only one transmitter for this signal, and it is in Bolder, Colorado.  If you wanted to avoid a radio signal from
Bolder, Colorado, your best bet would be to move to Maine or Florida. I happen to live in that latter state. Getting a signal here is hit or miss on the best WWV clocks. They also seem to only come with simple, LCD displays that cannot be seen at night -- at least not without pressing a button.

There are also some really cool-looking Nixie Tube clocks, and yes, some of them have options for modules that allow them to sync to GPS satellites. Again, assuming you sleep indoors, getting a reliable GPS signal can be a problem. These clocks also seem to cost upwards of 100 dollars, and that does not include the GPS module. Even at well north of $100, they seem to lack functions like alarms, and still have those piddly little buttons.

Yes, I looked at others, too. Some claim to sync to signals from local FM radio stations, others claim to be "factory set".  Where's that "Skeptical" emoticon when you need it?

I had always had the idea of building my own cool-looking Nixie or VFD clock. Then I happened across some hardware that made my idea a possibility...

Back to Contents


Many years ago, I came across some (even at the time) old, surplus, Alpha-Numeric, one-line, Vacuum Fluorescent Displays (VFD's). Interestingly, and rater advanced for the time, they accepted standard serial ASCII data as an input. I had long-since sold that old VFD, but when I searched for Nixie/VFD clocks, I thought of it. Then the "happenstance" -- A local surplus dealer had several of these same VFD's. I made a deal with him for a number of items, including one of the VFD's.

I took it home, and between some remembering, and looking up pin-outs of some of the chips, I got it working. I went back to the dealer, and told him that if he gave me another one, I would write up a basic data sheet. He agreed. The datasheet can be found here, and the display I am talking about is shown below:

Noritake VFD

20 Character by 1 line VFD.

More Technically, the displays are made by Noritake, and they still use the "itron" brand in relation to the Dot Matrix VFD's they make now. They seem to bear various part numbers, and there also seem to be variations with different branding.

UPDATE: I have also obtained a 1 line, 40 character Dot Matrix Display. I have a crude fork of my code for a 40 character display with upper/lower case. This code can be made available upon request.  See Google+, or Linuxslate forums link below.

So I had my display -- a Cool, Retro-looking 13 Segment display tube, Alpha-Numeric, and very easy to send data to.

But what do I drive it with? A Raspberry Pi is more power than should be needed for an alarm clock, but Arduinos don't typically have networking capability without adding "shields". I was searching for WiFi modules for the Arduino when I learned about something called NodeMCU, and the ESP8266 module that NodeMCU is based on.

NodeMCU looked like the perfect therapy for my Smart Clock obsession. Built-in WiFi, Serial pins, and plenty of other pins for things like PCM sound output. It also had GPIO that buttons could be connected to, but a Smart Clock shouldn't have buttons.

NodeMCU also met all the other criteria. It has it's own development tool, called "ESPlorer". ESPlorer is both simpler and more elegant than the Arduino environment, and it is written in Java, so installing it on Linux was trivial.  Also needed was a firmware loader called ESPTool.  That uses Python, so installing that on my Ubuntu Linux box was even -- "trivial-er"?

One last thing -- the actual NodeMCU development module costs $6. -- and that was without shopping around. If you don't impulsively click "Add to cart" on your favorite retailer, or if you buy in quantity, you can find them for just over $3 each.

NodeMCU Dev Kit

The NodeMCU Dev Kit is available
for only a few Dollars.

The only other thing I needed was a simple audio amplifier and small speaker for the uploadable alarm tones. I went back to the surplus dealer, and spent another $5 for the speaker assembly from the bottom of a computer monitor. It contained a USB-powered stereo amplifier, and 2 speakers. I wasn't planning on stereo, and currently, NodeMCU only supports one PCM output, so I cut off one speaker, and wired up the input that corresponds to the remaining channel.

20 Digit VFD that is bigger, brighter, and cooler looking than the $100 VFD clock kits?  Check.  Total cost: less than $25.

Wiring it all up is almost literally a 5 minute job. Other than power/GND, only 2 of the NodeMCU pins are needed.:

NodeMCU pin (Clock wise from USB connector
Connected to
5V power To/from VFD power pin, USB power cable, and the Audio Amplifier
Audio Amp Ground
D2 (used as PCM out)
Audio Amp Signal
D10 (used as TX0)
VFD Serial Input
VFD Serial Ground

At this point, a few Electrical Caution Statements are appropriate:

NodeMCU is strictly a 3.3V device with the exception of the 5V Power Input (Vin). Do Not connect the VFD 5V serial TX to NodeMCU's RX0 (D9) as damage will occur. Since D2 is an output, we can get away with connecting it to the VFD's serial input without a level converter. The serial input will accept anything over ~2.5V as a logic "1", so 3.3 volts works fine.

Also, do not attempt to power the VFD using the Vin pin to supply 5V from the USB connector on the NodeMCU Dev Kit. There is a USB blocking diode that is meant to keep the 5V input from backing up into the USB connector. Attempting to power the VFD through the blocking diode will exceed it's current rating.

Lastly, the 3.3V D2 PCM output overpowers the line level (1V P-P input) of the audio amp. Your amp needs to have a capacitively coupled input, and some input attenuation (possibly in the form of a volume control), or you need to build the audio filter shown in the documentation for the NodeMCU PCM function.

Back to Contents

Building and Installing the Firmware on NodeMCU:

Like a lot of things in the geek world, the term "NodeMCU" is used (correctly or not) to refer to several things. It can mean the NodeMCU Dev Kit, which in itself is really just an ESP8266 "12E" module and a FTDI USB serial port soldered together, or it can refer to the adaptation of Lua Script that is meant to run on the aforementioned Dev Kit.

NodeMCU is not an "operating system". It is an Lua Script Interpreter (written in C), customized for the ESP8266.

We need to build a custom version of NodeMCU. By far, the easiest way to do this is by using the On-line Automated Build Tool that is run by a developer known as Frightanic. NodeMCU has very limited RAM, and our Lua Script code will share this limited RAM with the Lua Interpreter so it is vitally important to include only the NodeMCU modules we really need.

Enter your email, Select ()Master<>, and then check only the following modules: end user setup, file, net, node,PCM,RTC time,SNTP,timer, UART, and WiFi. (10 modules).

Click "Start your Build", and wait. When you get the download links (check your spam folder), download only the Integer firmware image.

Given that you probably don't know what is on your NodeMCU Dev Kit when you get it, I recommend doing a full chip erase.  This means that you will also need the esp_init_data_default.bin file from the current version of the NodeMCU SDK patch. Download the patch, and ignore everything except that one file.

Follow the instructions on the NodeMCU Flashing the firmware page where it talks about using  Do the --port <serial-port-of-ESP8266> erase_flash

command and the next line with both the integer firmware from the automated build and the esp_init_data_default.bin file. --port <serial-port-of-ESP8266> write_flash -fm dio 0x00000 <nodemcu-integer-firmware>.bin 0x3fc000 esp_init_data_default.bin

Power Cycle (not just reset) the NodeMCU after a successful firmware flash.

Back to Contents

Software Design:

To address the issues mentioned in the introduction, My clock had to meet several design criteria.

1.  It had to display the correct time -- without setting.  This is easily accomplished with SNTP.
2.  Since (1) requires an internet connection, why not have the user interface be web-based -- Just like routers, switches, and other "Internet of Things" (IoT) devices.
3.  It should have selectable and changeable ring (alarm) tones.
4.  As mentioned it should have multiple alarms, and alarms that can be set for weekdays, a single day, etc.
5.  It should make use of the Alphanumeric capabilities of the display. This includes Day and Date, as well as other possibilities such as Weather, Stock Quotes, News Headlines, etc.

Here is a screen grab of the main settings page:


Back to Contents

Limitations of NodeMCU/ESP8266/Lua Script:

As mentioned, the entire computer that runs this -- with network interface and all I/O -- is about the size of a large postage stamp, and costs about $5. It is not a Octo core ARM device, or even a Raspberry Pi. It has to have limits in it's capabilities, and -- it does. It has 4 meg of flash storage in the form of a simple, flat SPIFFS filesystem.  It also has 128K of memory, but even that is not all necessarily available to our Lua program.

It has some other limitations -- the maximum amount of data that can read or written from/to Flash in a single operation is 1K. The maximum amount of data that can be sent over a single network socket is 1460 Bytes.  It's very easy to instantiate a web server, but without some trickery we are only ever going to serve very simple web pages. While it doesn't specifically support CGI, we can implement simple GET and POST operations.

Yes, we could do some fancier stuff with CSS, and perhaps some Javascript, but serving that is going to take precious RAM from our functional code.

Back to Contents

Code Walk-Thru:

NOTE: A Zip File of the Lua Scripts will be made available as Open Source shortly.
UPDATE: Code Now Available via the Forum.

3 Simple and inexpensive modules: VFD, NodeMCU, and an Audio Amp. Only 2 signal wires connecting them. The challenge is obviously not in the  hardware.

To save your time, I won't explain all of the code.  Also, it is not finished yet. (Is software ever finished?).  I'll go over the basic architecture, the start up sequence, and some of the things the code needs to do to implement a viable clock. This project was an excellent way to learn NodeMCU/Lua Script, and much of my motivation for publishing this is to help others learn.

NodeMCU Lua contains a simple, but powerful function called enduser setup that is specifically to allow NodeMCU IoT devices to be easily linked to an existing WiFi network. A single command creates a WiFi Access point which is running a Web Server that allows us to find a WiFi base station, submit credentials, and join. Show me another language that does that in one line.

When NodeMCU boots, it looks for a Lua Script called init.lua. If present, it is executed. The documentation I read suggested putting very little code in init.lua, and including a delay so that in the case of an error, your NodeMCU is not stuck in a boot loop. So in my implementation, init.lua simply waits 10 seconds (that is actually excessive), and then calls eus2.lua.

eus2.lua checks for a connection to a WiFi access point (AP), and if there is none (in other words, an error connecting to WiFi), it invokes the enduser setup function. If it is assimilated, it simply displays the IP address, and kills off the enduser setup server.  If you leave EUS running, it will continue to advertise itself as an access point even when it is connected to the building WiFi.

eus2.lua in turn starts the actual clock (vfd_clock.lua).  As mentioned I will not explain the code line-by-line.  Lua Script is very simple, and very human - readable.

In short (and in order), vfd_clock.lua contains the following:

-- Initialization -- including simple "databases" (Lua "Tables", which in this case are actually just arrays.) for:
        The Names of the Days of the Week
        The Names of the Months
        "Spring Forward" Dates for the US through the year 2029
        "Fall Back" Dates for the US through the year 2029
        Filenames for the available ring tones -- Note that this should be automatic, based on scanning the filesystem for properly encoded sound files.
        Defaults for things like Time Zone (GMT offset), whether or not DST is used, etc.  Some of these should be read from a configuration file.  See Section below.

-- The Sound Player -- This is a slightly compressed/simplified version of the example sound player code found with the PCM module source code.

-- The Web Server --  Again, based on the example, but with some little extras like a built-in 404 page. In the event of the user accessing the Internet Settings page, control is passed to another script that implements a dynamic web page that allows the user to configure things like a Static IP address. In the future, all of the settings pages may be handled in this manner.  At the very least, a dynamic page that displays the current settings is needed.

-- A "Daytime" client -- From Sample code one the ESP8266 Forum. This is currently only called once on boot, and only used to determine if we are initially in DST or Standard Time.  The actual time retrieved is ignored for now.

-- The SNTP client -- Called initially and every hour to maintain time sync.  The ESP8266 real time clock (RTC) is set to the time provided by the timeserver.

-- HTML POST code -- In essence, this is our CGI processor.  It retrieves the content of a HTML POST from the web server, and processes the web forms. Currently, it also saves the Alarm settings to a file so that if there is a power interruption, or a reboot, the alarm settings will be maintained. In the future, it should also save the basic clock settings, including the selected display mode.


Alarm Settings Page - Settings are handled by a function
that is called when there is an HTML POST.

-- A timer function that runs once every second --  As you can probably guess, this is the main functionality of the clock.  It has to do a number of things, including:
        Determine what "Page" we are on. With a one-line display, this means which line of text, or message we are displaying.
        Compute local time by adding both the GMT offset, and the DST offset.
        Check for Spring Forward, or Fall Back, including handling things like the repeated hour that happens each year on the "Fall Back" day.
        Format the time for each of the 4 (currently) possible displays:
                24 Hour time with Clock only
                12 Hour time with Clock only, including things like the concept of 12AM (instead of 00hundred hours), and AM or PM
                24 Hour time with Day and Date
                12 Hour time with Day and Date, including things like the concept of 12AM (instead of 00hundred hours), and AM or PM
        Determine if there is a sync or error message page, and display it.
        Determine if there is an alarm message page, and display it.
        In the future, this may include code to determine if we need to display a Weather information page.
        Check for someone pressing "Alarm Cancel" or "Alarm Snooze".  We need to check this every second in order to respond in a timely manner.

-- A timer function for changing the display every 5 seconds --  This allows us to alternate any alarm or error messages on a 1 line display with the current time. In the future, this may include Weather information.

-- A one minute timer function --  This checks for alarms, and alarm time out, or snooze time out.  Note that by doing this every minute, instead of every second, we save processor power, but it means that an alarm set for (for example) 07:00AM will not sound until some number  seconds into that minute. An alarm can be at most 59 seconds late.

-- A one hour timer function -- Currently this is only used to synchronize the RTC. In the future, this may include retrieving Weather information.

-- Another, separate Lua script (inet_set.lua) -- This runs anytime the user requests the internet settings page. This page saves static IP settings. If DHCP is selected (default), the static IP settings file is deleted.

Back to Contents

Current Status/Bugs/Issues:

The clock is currently working with Time or Time/Date display. One Alarm is supported, and Alarm time/functionality is not lost if there is a power outage or reboot. It is usable as an alarm clock.

  1. The first and most important issue has nothing to do with the functionality of the Hardware or Software. It is actually just a consequence of using a VFD.  The mentioned VFD's are very bright, and apparently not dimmable by hardware of software. Sleeping (or attempting to sleep) with blue light is actually a health hazard. Solutions: 1.) I have 1 green filter specifically for these displays. Green should absorb some of the blue, and pass red and yellow.  2.) I have ordered some red plexiglass filters. There is plenty of red light to provide a visible red display while blocking most of the blue. The case for prototype #2 will use this red glass.  3.) I have purchased (again, surplus) 1 x 40 character dot matrix VFD display that works the same way, and allows for much more information on the screen. It is not nearly as bright, and it does have a simple hardware dim function (that could be connected to another NodeMCU GPIO pin.)  4.) I have not found any similar serial Red LED displays, except for very large ones. I will continue to search.
  2. Clicking noises in the speaker.  Solutions:  1.) Implement the voltage divider/filter (hardware) mentioned in the PCM Module files. 2.) Procure LM386 based amplifier modules, and if necessary, add the voltage divider/filter.
  3. Basic settings not saved across a re-boot.  Solutions: Save more settings to flash.
  4. Only one alarm implemented. Solutions: Implement second alarm.  If this causes memory issues, then other code will have to be optimized. UPDATE: The 2nd alarm function has been added.  Overlapping alarms will probably cause "something bad" to happen.
  5. Weather currently not implemented. Solutions: Implement Weather if memory allows. This will be implemented using Open Weather Map XML files, and an HTML client based on the example code shown on the Wikipedia page for NodeMCU, instead of using the HTTP client module, as using the module seemed to cause more memory issues.
  6. Settings Pages not filled in with current values. Solutions: 1.) Implement more settings pages as dynamically created HTML; possibly in separate Lua Scripts.  2.) Write individual lines to Settings pages to capture current values.
  7. Impossible to see current alarm settings, only possible to re-send them.  Solutions: 1.) See above (Issue 6). 2.) Implement a single, dynamic "Current Settings Page.
  8. Only 1 attempt on boot to contact the SNTP server.  If this fails, the unit will blink "LAST TIMESYNC FAILED", and an incorrect time for up to an hour before attempting again to contact the server. Failed Sync's in general do not result in a re-attempt.  Solutions: 1.)Re-try if an initial sync fails. 2.) Be more aggressive in re-trying failed time sync's in general.
  9. Various memory issues.  Solutions: Try compiling the scripts.
Backto Contents


The bright blue light, and clicking speaker still prevent me from using this as my bed table alarm clock, but it is functional, and with just a little more effort these 2 main problems will be solved. Learning Lua script has been fun, and Lua/NodeMCU is a great way to get into hardware/software development. It's got the lowest barrier to entry of any possible learning environment, even Arduino. Unless used simply as an internet connection for a more powerful processor, it is limited to relatively simple projects, but these projects are not limited to IoT devices. NodeMCU would be a great way to introduce children to programming, robotics, etc.

While I could certainly be accused of over-engineering something as simple as an alarm clock, I really think there is a place in the home for such a device. Today, peoples telephones are their interface to everything, and going for your phone to silence your bed side time display is not as far-out as it may have been just a few years ago.

Back to Contents


Discuss this article on the Forum Google+ article for this post. Post public questions/comments/suggestions here.

All trademarks/tradenames are the property of the companies that own them.