The purpose of this project was to design a digital LED clock influenced by the large clock on the building at 200 Water Street in lower Manhattan. Jason has always wanted to create an homage to this clock since he finds the design inspiring. Jason and Eldar decided build a wall clock for their final project using the 200 Water Street Clock design. They were motivated to create a functional piece of technology that adds color, vibrance, and time to any room.
This project was implemented on a PIC32 Microcontroller using the Small Development Board used in class, that has an external oscillator keeping time with accuracy of ±1 minute per year, and the WWVB receiver for setting the time whenever the signal can be received. Time is a displayed on a panel in front of 72 Adafruit DotStar LEDs. The clock takes input from three Push-Down Buttons that select the mode of operation: Time, Date, and Temperature.
The WWVB signal is transmitted one bit per second, taking 60 seconds to send the current time of day and date within a century. Information is encoded using amplitude modulation with the following scheme:
Markers are sent during seconds 0, 9, 19, 29, 39, 49 and 59 of each whole packet. The start of the second of two consecutive markers indicates the top of the minute, and serves as the on-time marker for the next frame of time code. Markers are important to allow receivers to properly frame the time code.
Figure: WWVB Code2
Hardware and Software Trade-offs:
Originally the focus of the project was on updating the clock using the WWVB signal relying on hardware and its integration. However, after more research and testing it was clear that the signal is quite unreliable due to the fact that it is a very weak signal and too much noise is injected into that bandwidth by our location on a highly technically advanced campus. The only antenna/receiver that was available for purchase was chosen and there were no other hardware options to explore at that point. At this point the focus was shifted to add more hardware in the form of an oscillator - which dramatically improved the accuracy of timekeeping and nearly eliminated the need for updating using WWVB. The updates can now be also provided by an Arduino that sends the correct time to the PIC32. Additionally, since the project was inspired by a piece of visual art, we put in extra effort in making it aesthetically pleasing, e.g. using wood instead of cardboard and eliminating the leaking of light between the LEDs by creating individual housing for each LED. DotStar LED strips were chosen as opposed to the NeoPixels, because they were easier to program, had better documentation, though they were more expensive. Additionally their implementation was complicated due to having to solder them with short wires having researched possible problems with using longer wires between cut strips. Additionally a level shifter had to be added, because the DotStars Clock and Data inputs were 5V and PIC32 outputs were 3.3V.
No existing copyrights or trademarks or patents were found similar to our design. The digital clock on the building at 200 Water Street was designed by Rudolph de Harak, an American modern designer who worked in numerous mediums. Our design is an imitation of that of the artist’s, and not original in the visual implementation. However, it is original in it’s electronic and hardware implementation.
High Level Block Diagram:
Figure: Block Diagram
Figure: Simplified Schematic
In order to receive the WWVB signal, we purchased a 60 KHz WWVB atomic radio controlled clock receiver from Universal-Solder. We chose this part because it is the only WWVB receiver currently available on the market. The receiver is attached to a 100 mm ferrite core antenna.
DotStar LED strips were purchased from adafruit to light all the number of clock. 30 LEDs per meter strips were purchased in order to space out each in its own box. The DotStars supported 8-bit RGB color, which allows us extreme flexibility for color choice. The other reason we decided to use the DotStars was for their simple and reliable communication protocol via SPI.
For our implementation the clock numbers were displayed on the face of the clock ordered from left to write, top to bottom, wrapping around from the end of right row to the beginning of left. In order to wrap the LED strip, however, we couldn't have long wires stretching across, and therefore, wrapped the LED strip around alternating sides of the box, see table below. Clock Number (in red), LED number (in blue):
Figure: DotStar Address Table
However, our implementation was more scalable than creating a table and saving it as a constant. We created functions to go from hours and minutes to LED location using modular arithmetic. Thus, if this project were implemented with more LEDs, we could easily extend our functions to address the additional LEDs added to the matrix.
The DotStar strips were driven and powered by 5 V. Powering them was not an issue since all our other components also used 5 V. But in order to drive them from the PIC32, we were required to use a voltage level translator (74LS125AN)7 to convert the 3.3 V signal to 5 V.
Figure: Voltage Level Translator3
The DS32KHZS#-ND 32.768kHz oscillator was used in order to implement the Real Time Clock Calendar RTCC functionality.. This specific value is convenient because it is exactly 215 and makes a 16 bit timer overflow exactly once a second. The specifications of the oscillator indicate that it’s error is only ±1 minute per year while operating under moderate climate temperatures (0 - 40 ºC). The oscillator outputs a 5 V square wave, that is connected in series with a 620 Ω resistor to pin12 on the small board. Despite being a 5 V signal the current in the oscillator’s output is only 150 µA. We decided to not use a voltage divider or level shifter to guarantee the fastest time.
In our project, nearly all devices use 5 V power. We purchased a 25W single output power supply made by Mean Well. It takes 120 V AC from a wall outlet and outputs 5 V DC. We decided to use a 25 W power supply to give provide a maximum of 5 A of current to power DotStar strip, which can pull a maximum of 4.32 A.
When designing our project, we wanted to add functionality in addition to time keeping. We settled on temperature because it is very easy to display room temperature in degrees celsius using the minute boxes. We used a LM35 analog temperature sensor to read ambient temperature. The LM35 uses a linear scale factor of +10 mV/°C and it is has ensured Accuracy of 0.5 °C at normal room temperatures (25 ºF).
The housing was built out of a combination of plywood sheets and softwood planks. The front panel was a piece of ¼” plywood that was laser-cut at the Cornell RPL to represent the numbers, it was later drilled from the front and the back to install the buttons. The back face was a piece of ¼” plywood that was drilled in the back for the ventilation of the power supply. The sides of the frame were half inch thick pieces of softwood, drilled on the bottom to hold the power supply in place, and on the left for the 120V AC chord path. The AC chord consisting of line, neutral, and ground cables was additionally secured by zip-ties on either side of the left wall to ensure that if it were pulled it wouldn’t come out of the power supply-causing electrical damage and endangering the user. The LED strips were hot-glued to the back face. A housing was created for each separate LED light in order to prevent light leaking into other numbers when diffusing onto the face. Long horizontal ribs (6 of them) were made out of ¼” plywood and cut on a band-saw gto fit into the box. Short vertical ribs (66 of them) were laser cut out of pieces of leftover scrap ¼” plywood. The rips were fitted in to house the lights and hot-glued in place.
Figure: Clock Front Panel
The WWVB transmits data by sending an amplitude modulated signal. The length of time the pulse is low determines the value of the symbol being transmitted. We use an input capture module to trigger on the first falling edge and then on every edge after. The input capture begins on a falling edge because the length of a WWVB pulse is determined by the time the signal is at -17dB, which in our case is a digital low value. We set up an interrupt service routine that records the time for a falling edge (the first signal that causes the input capture to trigger) and store sit into an integer, capture1. The input capture will then wait until it sees the next edge (which will be a rising edge) and it records the current time and stores it into another integer, capture2. Then we subtract capture1 from capture2 to calculate the length of the pulse, pulse_time. Additionally, increment counter that keeps track of symbol number in the current transmission. Several flags are also set here. For the falling edge we set ready to 0, indicating that time is not ready to be change. When it receives the rising edge, ready is back to 1. In the timer thread, the new symbol and time can only be set when ready is 1.
The input capture module is not able to determine whether the edge it is capturing on is high or low, therefore we set a bit back and forth to indicate which type of edge is next. This is possible because we defined the first trigger to be on a falling edge.
The input capture is very useful for calculating the time of pulses small pulses using the internal timers. Since we are calculating times in 100’s of milliseconds, even setting up an internal timer with the largest prescaler value when the processor is running at 40 MHz is not sufficient. Therefore, we opted to use the millisecond timer from protothreads12, PT_GET_TIME(), to calculate the time pulses. We feel that PT_GET_TIME() is sufficient because it takes approximately five weeks to overflow. If it does, overflow while decoding pulses, that pulse will be an error, and the Frame will need to restart.
The timer thread is the heart of the entire program. It begins by checking to see if the input capture has triggered twice, starting with a falling edge followed by a rising edge. If it has, it will then use pulse_time to determine the symbol: 100 ms > pulse_time > 300 ms is a 0; 400 ms > pulse_time > 600 ms is a 1; 700 ms > pulse_time > 900 ms is a marker; Otherwise it sends an error. The symbols are then stored into an integer. If a marker symbol is received, we check if the previous symbol was also a marker, which indicates the start of the next frame. If this is the case, we set the bit number, bit_n, to 0 keep track of the position in the frame. We also check the capture_count to determine if time has been set. If has not been (capture_count == 0), we set capture_count to 1 and call input_time(). Calling this first input_time() sets the time and date to default values (date: 1/1; time 1:02). The yellow seconds indicator will begin to run, and it can be used as a countdown to when the entire dataframe will be received and the proper time and date are set. If the time is being recorded but has not been set (capture_count == 1), we set capture_count to 2 and call input_time(). Calling input_time() now will set the time and date to value it decoded from the WWVB signal.
After the symbol is determined from the pulse duration, the time is set by calling set_time(). The set_time() function takes in two parameters, the symbol and its number in the frame. We use 60 individuals case statements to based on the symbol number to decode time. For example, bits 12, 13, 15, 16, 17 and 18 in the frame are used for the hour values, 20, 10, 8, 4, 2 and 1 respectively (bit 14 is left at 0 ). If the sequence is:
The hour will be 20*0 + 10*1 + 8*1 + 4*0 + 2*0 + 1*1 = 19 or 7 o’clock pm. We also use the set_time() function to check for errors in the received symbols.
The input_time() function mentioned above is what assigns the time and date to the RTCC structures (tm and dt). The function begins by converting the day of the year value to month and day values. Then RTCC the structures for time and date are assigned using the values from the set_time() function and a macro that converts integers to BCD format:
#define int2BCD(a) ((a/10)<<4)|(a%10)
Next, we call RtccSetTimeDate(tm.l, dt.l) to set the time and date for the RTCC periferal. Lastly, we clear the all the global time and date variables for the next time set_time() is run.
The final part of the thread is case statement based the on the mode of operation (time, date, or temperature). Based on which mode is chosen, a corresponding LED function is called to set the DotStar LEDs. Lastly, we call write_pixels() to send the LED color and brightness data via SPI. PT_YIELD_TIME_msec(10) is called to allow the Button thread time to execute.
The DotStars use SPI to transfer the 32 bit LED data frames. The first frame is 32 bits of 0’s. Then each LED has its own data frame where the first 3 bits are 1, followed by a 5 bit value for the intensity level, then a 8 bit value for blue, green, and red. The last frame that is sent is 32 bits of 1’s, which indicate the end of transmission. Below is a visual representation of the protocol.
Figure: DotStar Protocol4
We use the example code from Bruce Land (brl4) Pixel Strip Displays example (link). The code contained a working SPI function to communicate with the DotStar LED strip of specified size and a data structure to format the color and intensity information of the LEDs. The SPI channel also outputs square wave clock signal for the SK9822 control IC in the DotStars can synchronize with the SPI. Once the LED data array is filled, write_pixels() is called and the DotStars are set.
Before write_pixels() can be called, the LED data array needs to be filled. We use three functions, time_to_led(), date_to_led(), and temp_to_led() to display the time, date, and temperature respectively, on the DotStars. For time_to_led(), parameters for the hour, minute and second are sent. Next we do calculate which LEDs are the hour, minute and second and write a unique color value to each of them, while giving the other LEDs a different color. The function is date_to_led() is identical to time_to_led() except it only takes parameters for month and day. The LED for month is set the same way that hour is set and day the same way minute is set. We black out numbers 00 and 32-59 since those are not possible dates. The last LED function is temp_to_led(), which receives a single parameter for temperature. The LED for temperature is then set the same way same way that minute and day are. An added feature of the temperature LED function is that the temperature “flickers” between the integer values to indicate the value that is nearest.
The real-time clock and calendar (RTCC) periferal on the PIC32MX250 is used to keep the time and date. It is able to keep accurate time using an external 32.678 KHz crystal oscillator. Without the external oscillator, RTCC would need to use the internal oscillator which is not very accurate (add spec?) In the config.h, we add #pragma config FSOSCEN = ON so the PIC knows to use the external oscillator.
Button Debounce Thread:
User input is received via 3 buttons that are connected to the Small Development Board. When depressed the buttons would short the PIC pins with Vcc through a voltage divider 680Ω/1680Ω from the 5V provided by the power supply resulting in approximately 3V. A protothread was used to “debounce” the buttons, to make sure that the input is read correctly. Every 30 milliseconds the buttons were polled to assess whether they had been pressed. The pins were set as digital inputs. Values from the previous poll were also kept to implement the following logic:
|Previous Value||Current Value||Logic|
There were 3 buttons that were implemented in this manner. The debounce functionality ended up being more robust than necessary as only the button presses were used to switch between the three different modes.
The real-time clock and calendar (RTCC) periferal on the PIC32MX250 is used to keep the time and date. It is able to keep accurate time using an external 32.678 KHz crystal oscillator. Without the external oscillator, RTCC would need to use the internal oscillator which is not very accurate. In the config.h, we add #pragma config FSOSCEN = ON so the PIC knows to use the external oscillator. The RTCC periferal is initialized in the main() function.
The main() function was used to set up the peripherals and threads that were used and described above. We set up the ADC channel (for the temperature sensor), SPI channel (to send the DotStar LEDs lighting data), Timer2 and InputCapture1 (to calculate WWVB Pulses), initialized the RTCC (To keep correct time using the external oscillator) and system wide interrupts. The SPI was set up for channel 2 and configured to send 32 bit values The SPI clock speed is divided by 16 to give small relative rise and fall times after the signal is level shifted to 5 V. Finally, we initialized the two threads, pt_timer and pt_buttons, and scheduled them to run in a round-robin format.
After waiting for over four and half weeks to receive the WWVB receiver, it finally arrived. Unfortunately, we had tremendous difficulty using it and were unable to pick up any trace of a signal in Phillips Hall. Since the deadline was approaching, we decided to use an arduino board to simulate the WWVB signal. We found code online11 to do this. The arduino code sends a perfectly clear pulse modulated signal, that PIC has not trouble decoding.
Figure: Arduino Signal
From a black box perspective, the signal from the arduino and the signal from the WWVB receiver look totally identical. The arduino is a very powerful tool that we used to test time all types of time and date inputs to fix all errors we could find. Unfortunately, we were not able to build a clock that sets the time with absolutely no user input. We plan to continue working on the clock and find a solution for receiving a WWVB signal.
The PIC is able to keep very accurate time. We have left the time run side by side several other clocks for several hour periods and have not seen any difference in the time. This is very important because wall clocks are meant to remain on and function without stopping.
Design of Housing:
The laser cut numbers on the panel of the display make the numbers very sharp and easy to read. The Wood box is very strong, but the ribs that separate the individual number boxes and prevent light from leaking are not totally even. This presents no visual problem, but it would be nice for front and back panels to be totally flat.
We designed the clock with the intent to upgrade and add new features and settings. Like any good engineering product, it has the ability to be upgraded and improved very easily. We compiled a list of additional functionality that we would like to implement in the future:
Add Self Calibration for the Oscillator
Add Better Error Correction - For instance, if a marker is the symbol that is supposed to be present at a specific bit, an error will thrown and the entire data frame will be discarded.
More Details Using the LEDs
AM / PM lighting effects
Use ours as a decimal place for temperature sensor
Adding Tetris or Connect 4 games
Faster Time Setting
Using location in frame to set time faster
Throughout all of the work that has been done on this project all of the limitations were checked and never exceeded. Before using any circuit it was checked for shorts and all soldering connections were double checked before applying voltage. All the data sheets were consulted and double checked to ensure correct connections and safe usage of materials and devices. We are not aware of any factors that might endanger the public or the environment throughout the use of our project. The power supply and it’s temperature and output were tested for prolonged periods of time to ensure safety. The cord connecting the power to the wall AC outlet was secured to the best of our ability to ensure that if it is pulled it does not apply stress to the connection with the power supply which could cause unsafe and unpredictable behavior. All of the concerns and problems faced by our team were disclosed to the instructor and the TAs, there were no conflicts of interest. No bribes of any kind were accepted by any of the team members. All of our findings throughout this project were reported to Bruce Land, the TAs, as well as fellow students. Before undertaking any technological tasks qualifications were taken into consideration and every time there was doubt about training or experience the TAs and the Instructor were consulted prior to carrying out the task. Honest criticism and errors were accepted and corrected respectively, feedback was sought throughout the project from teammates, fellow classmates, TAs, and Instructors. All of the resources that were used by the team were properly credited. All persons on the team were fairly treated regardless of their origin, beliefs, creeds, abilities, and/or identities. Any actions taken relating to this project were taken with caution to ensure safety of all those involved, others, their property, reputation, and employment. Assistance was provided by the teammates to better each others professional development and support them in following this code of ethics.
There are no legal considerations that we are aware of in regards to this project. If this were to become a product that we were going to sell, we would need a legal consult on whether it is too similar to the building to be an original design and whether the artist/their family has that design copyrighted.
Safety and Usability:
The LED strip can draw a maximum of 4.32 A of current (60 mA per LED), which our 25 W power supply can provide. This can raise several concerns about the temperature when running for a long time. Changing the different functions will be done by pressing one of the three buttons on the bottom bar (see diagram ??). A model of the panel is given in ??. The antenna for the WWVB receiver is currently not in the clock because we unable to receive any type of signal from it. But if we are able to pick up a WWVB signal, it will most likely be placed at the top of the device.
The group approves this report for inclusion on the course website.
The group approves the video for inclusion on the course youtube channel.
Layout and Implementation
Scrap Wood Acquisition
Case Sides and Panels
LED Strip Placement
LED Individual Housing
Power Supply Integration
AC Cord Integration and Securing
Hardware Soldering and Testing:
Voltage Level Translator
DotStar LED Strips
Code Implementation and Testing:
DotStar LED Addressing
Temperature Sensor/ADC Debugging
WWVB Code Acquisition and Testing
Final Report Sections:
Summary of What and Why
Hardware: Power Supply
Software: Button Debounce
Software: LED Functions
Final Report HTML Implementation
Front Panel Design (CADed and Cut at Cornell RPL)
Hardware Implementation and Testing:
Small Development Board Assembly
Voltage Level Translator
Code Implementation and Testing:
DotStar Example Implementation
DotStar Addressing Debugging
WWVB Signal Decoding
Button Debounce Testing
Final Report Sections:
Summary of What and Why
High Level Block Diagram
Hardware: WWVB Receiver
Hardware: Temperature Sensor
Software: Input Capture
Software: LED Functions
Safety and Usability