-
Notifications
You must be signed in to change notification settings - Fork 4
Home
View a video tour of the word clock at YouTube.
My friend Tom and I always exchange Christmas presents. This year I was having trouble coming up with an idea. I'd already given him a hot air stirling engine and a MOVA Globe. What to do? Then I remembered the cool wooden clock kit he'd sent me a few years ago and thought, how about a word clock? But wow, the good ones are expensive. And the cheap ones look... cheap. Maybe I could build one. The project turned out to be a tad bigger than I anticipated; I did finally get the clock to Tom in time for his birthday in March.
Finishing one clock creates a strong incentive to build a second, to improve on all the inevitable flaws in the first. So it was serendipitous that my friend Doug's birthday is in late February. After that, well, Beth and I needed a clock, too.
So the journey began.
I got a little carried away adding features. A summary:
- Show the time in five-minute increments, along with the day of week.
- Display yearly birthday messages to my friends and their spouses.
- Adjust the clock brightness based on the ambient light.
- Back-light the perimeter of the clock. Use the lights to simulate sun rise and set, and to show the sun's current azimuth as it moves through the sky.
- Automatically calculate the clock's orientation relative to the real sun.
- Configure the clock via a web site.
Some folks have asked why I didn't include GPS. The thought crossed my mind. But I decided it might not work reliably indoors.
The clock face is a 12 by 12 grid of characters. Each clock is customized to display an annual HAPPY BIRTHDAY message to the owners. The bottom row shows the day of week and the number of minutes elapsed since the currently-displayed time.

When the birthday message displays, the outer band of letters and backlighting perform a theater marquee animation, as shown below. The message words must fit within the marquee, on the inner 10 by 10 grid. The message also looks better if the text is centered and evenly spaced vertically, more or less:

To keep things interesting, I added these constraints:
-
All filler letters must also form English words, either alone or combined with other words. My favorite word is repast.
-
In a given column, no adjacent rows should have the same letter. In the end, I was unable to avoid one column where the letter T repeats once:
It was rather tedious to iterate the design and repeatedly check the column rule manually, so I wrote a Python program to report violations.
Laying out the Tom & Sandy and Buzz & Cap'n (for Beth and me) clocks was fairly easy, but Doug & Marguerite was challenging because Marguerite fills a ten-character row. I had an aha moment when I realized I could place eleven vertically along one edge, at which point everything fell into place:

My daughter Jessye, who is a graphic designer and Adobe Illustrator guru, created the face graphic. She chose the font Space Mono.
It's critical that the letter spacing match that of the neopixel strips that are mounted behind the letters. So I had a full-sized "poster" of the graphic printed at our local Walgreens. The photo department clerk was quite intrigued. Here I'm checking the spacing:

It took several attempts to find a suitable fabricator and process for the face panel. I wanted a sheet of material printed with an opaque black background and translucent letters.
Signs.com seemed to offer the perfect solution: an image printed on frosted acrylic. But when I received my order, I discovered that the printing wasn't sufficiently opaque, allowing light from the neopixels to bleed through the ink and create a kind of halo around the letters.
I reasoned it might be possible to create an opaque image by sandwiching two panels together, with the image printed on the front of one and the back of the other. So I ordered a second sign. That's when I discovered that signs.com can't guarantee sufficient run-to-run accuracy to ensure that the two images would line up. With the letters aligned on one edge of the panel, they were misaligned at the opposite edge just enough to ruin the effect.
I discussed the problem with another sign fabricator, who suggested they could do two passes of ink on one sheet of acrylic. So I ordered a panel from them. Nope. Two layers aren't enough.
Finally, I found 858 Graphics, in San Diego. They offer a process in which the image is cut into vinyl, which is adhered to a sheet of translucent acrylic. The process is called kiss cutting. After the vinyl has been cut, a person carefully weeds the image to pull away the unwanted material. Brian Villa, with whom I worked, was as intrigued as the Walgreens clerk. He said they'd even be willing to cut a hole in the acrylic for the ambient light sensor positioned below the grid of letters. They were justifiably proud of their work, and sent me a picture of the panel just before packing it for shipment:

When I saw the photo, I happened to notice that the T in the second column of the eighth row hadn't been completely weeded. I email Brian to ask about it. They unpacked the panel just before it went out the door and pulled off that last bit of vinyl.
Viewing a panel from a couple inches away, you can see minor imperfections in the cutting. But from a normal distance, the panels look great. The only downside is that the panels are expensive, which is understandable given the labor involved.
The clock electronics consists of:
-
SparkFun MLX90373 Triple Axis Magnetometer Breakout
-
SparkFun ADXL313 Triple Axis Digital Accelerometer
-
Adafruit VEML7700 I2C Lux Light Sensor
-
SparkFun Bi-Directional Logic Level Converter.
I chose the Raspberry Pi because it would be easy to program and because it has a low enough vertical profile to be tucked behind the MDF back panel, where the clearance would only be 1/2 inch. For reliability, I chose a Transcend high-endurance 16 GB microSD card.
The magnetometer acts as a compass, so the clock knows where the sun rises and sets. The accelerometer measures the Earth's gravity, to determine the orentation of the magnetometer. The light sensor measures the room's ambient light, to adjust the clock brightness. And the level converter interfaces the Pi's 3.3 volt digital output to the neopixels' 5 volt data input.
All of the sensors use I2C interfaces and have excellent Python library support. The QWIIC connectors on the Sparkfun devices make it easy to connect everything.
The clock face has 12 strips of 12 neopixels each. The first design had an additional 4 strips of 12 neopixels around the perimeter for backlighting. In the newest design, I've switched to denser backlight strips to make the lighting more even. The new strips double the number of backlight pixels to 96, for a grand total of 240.
I wanted to be sure I could actually write a program that would control the neopixels, and that the Pi Zero would be fast enough to run the birthday message marquee animation. I also wanted to double-check the power requirements. So I breadboarded the electronics and hooked up two 5-meter neopixel strips, all powered by my Rigol bench supply:

Everything worked! I estimated the power requirement would be no more than 3 amps at 5 volts. Here's an initial test with the front panel. (Ignore the messy desk.)

The electronics and neopixels are mounted to a 1/2 inch thick MDF panel attached to the back of the clock face. The neopixels for the face are hot-glued into channels on the back of the panel, with a hole between each pixel and the letter it illuminates. The backlight strips are glued to the four edges of the panel.
I decided now was the time to get serious about improving the precision of my saw cuts. Rather than buying a table saw, I invested in a Festool track saw and multi-function table; I reasoned that a track saw would be safer. Below is the saw and table ready to cut a sheet of MDF. Note the UJK Parf Super Dogs being used to square the MDF. The table is very versatile; it can be used not just for cuttng, but for securing material for other work.

Another tool I acquired recently is a Carbide 3D Shapeoko, I used it to mill the back panel and other clock components. I designed the components with Carbide Create. Here is the back panel design rendered in 3D:

The Shapeoko specs claim it has a 16x16 inch cutting area. But that's a bit of a lie; to get 16 inches in the Y dimension, you have to hang the workpiece over the front of the Shapeoko bed. In one way, the overhang is a feature not a bug, because it allows you to mill small pieces that are taller than the maximum Z dimension. To make it easier to secure overhanging parts, I built a clamping assembly that hangs below the front of the mill.
It takes about two hours to mill the panel. Here goes:
-
Using the Shapeoko BitZero to precisely locate the design origin on the panel.
-
The Shapeoko getting started on the 144 holes.
-
Finishing the neopixel channels.
A strip of neopixels is basically a daisy-chain of WS2812 tri-color LEDs. The electrical design is quite clever. There's a single data line. To get things started, the controlling computer sends a special reset code, which tells all of the pixels to start expecting data. Next, it sends the data for all of the pixels in the chain as a stream of bits. Each pixel requires 24 bits, to set the brightness for its three red/green/blue LEDs. The first pixel grabs its 24 bits from the stream, and passes everything else to the next pixel. That pixel grabs its 24 bits, and so forth. Thus, the first data each pixel sees is its own. Here's a timing diagram from the data sheet:

I needed to chain together the twelve face strips plus the four backlight strips. It turned out to be incredibly tedious to solder the power, ground and data wires between each pair of strips. Watch the fun...
-
Here I try using forceps to hold each wire in place.
-
Wiring the corners of the backlight strips was even more fussy.
-
Still at work.
-
After a couple of hours I did manage to connect everything successfully.
I knew there had to be a better way to do all that wiring, and finally thought of the obvious solution: Design custom PCBs to replace the wires. I used KiCad lay out the boards, and ordered them from OSHPARK.
Here is the board I designed to daisy-chain connect a pair of neopixel strips:

Once I saw how easy and fun it was to design the PCBs, I got a little carried away. Here are a couple of boards to connect power to the strips at the end of the chain:

I also found a really cool spatula soldering iron tip. You can get these in various sizes. I purchased one that is exactly the width required to solder all three neopixel connections simultaneously:

Unfortunately, the tip doesn't fit my Hakko FX-888D soldering station. So I had to buy a Hakko FX-951. The 951 has some nice features: When you put the iron into the holder, the station goes to sleep. Later when you grab the iron, it wakes up and beeps when it's back to temp. Here are the original station and its new friend:

As soon as I ordered the PCBs, I realized I had only slightly improved on the old approach. And I hadn't done anything about other problems, such as the messy power distribution wiring:

In effect, I had designed a horseless carraige rather than making the leap to an automobile. It dawned on me that it would be much simpler to fabricate two long PCBs, one placed at the top of the panel to daisy-chain the data signals, and another at the bottom for data and power distribution. The bottom PCB could also include holes to connect the wiring for everything else:

With the new PCBs, wiring up the neopixels was a breeze. Here goes...
-
Super-gluing the bottom PCB to back panel.
-
Arranging the neopixel strips in the correct orientations and tinning the solder tabs. (Pro tip: It's quite easy to hook a strip up in the wrong direction if you aren't organized.)
-
Hot-gluing the strips to the panel.
-
Holding the strip in place as the glue sets.
-
Clamping down a jig to press the neopixel strips against the PCB. The jig consists of an older version of the PCB I repurposed, glued to a strip of MDF with the PCB hanging below the MDF just enough to press firmly against the neopixel strips.
A close-up of the jig. (This view is of the top PCB, which only has the middle/data tab tinned.)
-
Using the spatula tip to reflow the solder and make the connections. Sweet!
In the end there was only one bad connection, where a hair-width solder bridge shorted one data signal to ground. Luckily nothing got fried. (Pro tip: Use an ohm meter to check the connections before powering up.)
As I noted above, connecting the backlight neopixel strips at the corners was a fussy mess. At some point, when walking our dog, it dawned on me the obvious (as usual) solution is to round the corners of the MDF so that the backlight can be a single neopixel strip that wraps around the panel.
I was running short on time building the first two clocks, due in March. So I used a saw and file to quickly round the corners:
For subsequent clocks, I constructed a jig for routing the corners. I used the Shapeoko to mill the jig slot. I attached a flush trim bit with a 1/2 inch top bearing to the router, to follow the contour of the slot. The hole in the center of the jig is for setting the router plunge depth.

-
Clamping the back panel onto the jig.
-
Routing the corner.
-
Done!
By the way, that's version 2 of the jig. With the first version, after attaching the neopixel strip I saw that the last pixel ended up too far from the corner of the panel. I wanted it to be about the same distance as the first pixel at the other end of the strip. So I increased the corner radii just enough to reduce the total distance around the perimeter by 3/8 inch. Yeah, that was a little insane. And, yeah, I could have measured first.
The backlight neopixels are super-glued to the edge of the back panel in one continuous strip. Follow along...
-
I was able to use one of the PCBs from my horseless carraige design to connect power and data to the neopixel strip. Here I'm tinning the PCB and neopixel solder tabs:
-
Preparing to solder the PCB to the strip.
-
Holding the strip down while the solder cools.
-
Checking for shorts.
-
Along each panel edge, I applied a drop of medium-viscosity cyanoacrylate glue behind each of the 24 neopixel LEDs. Then I clamped the strip to the panel while the glue set.
-
The end of the strip, with the connector, glued into place.
-
The backlight power and data wired to the "main" PCB.
I asked Jessye to center the hole for the light sensor in the margin below the bottom row of letters, not considering everything that would be behind it. I ended up having to make room for the sensor by milling notches in the back panel and the bottom acrylic band (more about that later).
I also had to notch the bottom PCB to allow room for the sensor QWIIC cable to make its way to the electronics assembly. Truth be told, I overlooked needing a notch in the PCB and was a tad embarrassed when I glued it down and realized my mistake. So, of course, I ordered a second batch of PCBs with the flaw fixed.
The sensor also has to fit under the backlight neopixel strip, which rests about 2 mm above the face panel. To allow the sensor to fit under the small gap, I milled a well in the face panel to accomodate the surface-mounted components on the sensor PCB. With the well, I could glue the sensor flush to the panel. Milling was a delicate operation. If I cut too deeply, I would ruin the expensive panel.
Mounting the sensor:
-
With the clock face secured on the Shapeoko, setting the origin.
-
The finished well. Phew!
-
I was worried that the backlight might bleed through the face onto the light sensor. So I painted a black mask around the sensor with an acrylic paint marker.
-
Marking the center of the well.
-
Applying a drop of super glue.
-
Holding the sensor while the glue sets.
-
Done!
I mulled over various methods for attaching the acrylic face to the MDF back panel. Double-sided tape or other adhesives seemed like a bad idea. I wondered if I could glue flat-head machine screws to the acrylic that could pass through holes in the MDF. But I worried that the glue shear strength would be insufficient.
I finally came up with the idea of cementing a band of acrylic strips to the face and screwing the band to MDF brackets secured to the back panel. Machine screws would fit through holes in the band and screw into threaded standoffs inset into the brackets.
The band would protect the backlight neopixels and provide a place to mount the mode-control pushbutton. It would also make the face more rigid, so that it would sit flat against the back panel. The brackets would include a place to secure a wire for hanging the clock and a strain relief for the power cable. Felt pads on the brackets would protect the wall.
Here's how it turned out:

The drawings below show more clearly how the parts fit together. Note that the band strips are all the same length, and are just slightly narrower than the 1 inch combined height of the back panel and brackets. The standoffs fit securely between the brackets and the back panel.


I wanted the dimensions of the band strips to be precise, so I ordered them pre-cut from Professional Plastics. I milled the bottom band on the Shapeoko. The pushbutton shaft was too short for the 1/4 inch thick acrylic, but it was easy to mill concentric circles to allow the button to be inset in the panel.

Below is a photo of the bottom band being milled. I picked up a great trick for securing a workpiece from @Julien's superb book Shapeoko CNC A to Z: Place a strip of blue masking tape onto the bed and a corresponding strip onto the workpiece. For a secure seal, press the tape down with a roller. Apply a small amount of super glue to one of the tape pieces and then press them together. You'll have time to align the workpiece before the glue sets. Presto! Instant double-sided tape.

In the following steps I fabricate and mount the brackets.
-
I milled slots into the MDF brackets to accomodate the threaded standoffs and the picture wire and power cable. Because of the bracket length, I had to mill them in two passes. Here, I've milled one end of the top bracket and flipped it to mill the other end:
-
Super-gluing the standoffs into the ends of two brackets.
-
I built a jig to precisely position the brackets on the back panel. Here, the jig is clamped in place and I've applied glue for the bottom bracket. Note the notch in the jig to make room for the power cable, which will loop through slots in the bracket.
-
Tacking down the bottom bracket.
-
Repeating the process for the top bracket.
I attached a wire to the back panel, for hanging the clock from a picture hook.
-
Here, I've secured the left side of the wire to a screw eye and am setting the length to secure the right side.
-
Applying heat to the heat shrink tubing, with a bit of aluminum foil to protect the neopixels.
-
The completed wire assembly.
-
Gaffers tape to protect the neopixels.
Next I needed to drill holes into the side acrylic bands for the screws that would secure them to the brackets. To locate the holes, I built a jig to measure the distance from the end of each band to the center of each standoff.
-
Using a square to transfer the standoff center to the jig. I've drawn a line for the right standoff and am now doing the left one.
-
I've moved the jig to my drill press and drilled the first hole.
The moment of truth. Does it all fit?
The band is inset from the clock face by 1/2 inch. To set the distance, I built a couple of jigs and clamped them down so that two adjacent edges of the face pressed against them. Note that the jigs have overhangs that the face slides under.
-
The face pressed against the jigs.
-
The back panel, with the side bands attached to the brackets, resting on the face. The far band is pressed against the jig overhangs to set the 1/2 inch spacing.
With everything in position, I cemented the near band to the face. Next, I rotated the assembly 180 degrees and cemented the other side band. Finally, I taped the top and bottom bands into position and cemented them.
As I iterated clock versions, I improved how the electronic parts are wired and mounted.
For the first clocks, I hard-wired everything.

I also mounted the components directly to the back panel. What a mess!

For the new, improved design, I created a single assembly by mounting everything to a piece of acrylic.
-
Rather than soldering a gazillion wires to the Pi, I attached a strip of 90-degree headers, with jumper wires connecting the Pi to the other components.
-
Soldering jumper wires to the pushbutton, with heatshrink strain reliefs.
-
The electronics assembly mounted to the back panel. I ended up gluing the level converter to the panel rather than mounting it to the acrylic. Next time, I'll glue it to the acrylic.
I chose a 5-volt 4-amp power suply. I replaced the supply's barrel connector with an Anderson Powerpole connector. (Powerpoles have become the defacto standard low-voltage connectors in the ham radio community.) I built a zip cord jumper cable to connect the supply to the clock.

These photos show an earlier version of the clock.
-
I built a shipping case out of wood strips and cardboard. Picture hook included (in ziploc bag)!
-
I was worried that a jolt during shipping could cause the heavy MDF back panel to shear the acrylic bands from the face. So I taped in wooden blocks to transfer any force from the back assembly to the shipping case, effectively allowing the clock face to float in the case.
-
It was amusing to discover that I hadn't made the bottom slot wide enough to lay the power supply down flat. Cutting a hole in the cardboard solved that problem.
I wrote the clock control program, wc.py, in Python. It runs as a systemd service. The program uses the asyncio framework to manage multiple real-time tasks in a single thread.
The clock has a web page, shown below, where you can:
- See real-time information: The compass orientation, ambient light and clock brightness.
- Change the display mode.
- Configure various settings, such as the clock's latitude and longitude, brightness, and WiFi credentials.

You can use the Futz With Brightness dialog box, shown below, to view the clock as it would appear at noon and midnight, play with the brightness, and save new min or max brightness settings.

When first setting up the clock, you need to configure it with your home Wifi SSID and password (aka pre-shared key). To view the clock's web page and enter the settings, you first put the clock into Hotspot Mode by pressing the pushbutton at the bottom of the clock frame for three seconds. In hotspot mode, the clock sets up its own WiFi hotspot, with the SSID wordclock and password wordclock. You can then connect any device, such as your iPhone, to the hotspot and browse to the clock's web page, at wordclock.local (or http://10.0.0.1). After you enter your own Wifi SSID and password on the web page and click Save, the clock reverts to client Wifi mode and connects to your Wifi network.
I found a handy script for switching the Pi into and out of hotspot mode. The script was written by rudiratlos and is named, appropriately, hotspot.
You can configure the clock to adjust its brightness based on the current ambient light. You set four values: The min and max ambient light in lumens, and the min and max clock brightness as a percentage of full on. As shown in the graph below, the brightness varies linearly within the min and max ranges. The brightness never exceeds the specified min and max values if the ambient light goes outside the specified range.

The program adjusts the brigtness simply by scaling all neopixel RGB LED values by the calculated factor. To prevent the clock from flickering, the program damps the light sensor output by averaging the values over five seconds.
I suspect that the luminosity of the LEDs isn't a linear function of the scaling, and that each color has a different non-linear response. And then there's the whole question of lumens vs. perceived brightness. For version 2, it would be interesting to measure the actual brightness of each color as a function of the input value and build a corresponding mapping into the software.
The magnetometer reports magnetic fields in three dimensions, as three signed integer values. The outputs vary across devices, due to sensor zero deviation and scale-factor errors. In addition, a compass is affected by both variation and deviation.
Variation is caused by the the differing positions of the geographic and magnetic North Poles, and is determined by your position on Earth. The clock uses the Python geomag package to obtain the angle (the declination) to add to the magnetic reading to calculate true North at the clock's location.
Deviation is caused by nearby metallic objects. Hard iron objects are those whose relative position remains constant, such as a hunk of metal attached to the clock itself. Once you've compensated for the effect of hard iron, the compensation never changes. Soft iron objects move relative to the clock, and must be compensated for dynamically as the compass moves. I chose to ignore soft iron effects.
Compensating for intrinsic device errors and hard iron is pretty easy. You simply move the device through all possible orientations and record the min and max values reported for each axis. Assuming the hard iron effect doesn't change, you only need to do the compensation once. The compensated origin is then the average of the min and max values. A normalized measurement is simply the raw value minus the compensated origin. The following diagram might make all of this clearer:

I wrote a calibrate program to "factory-calibrate" the compass. The program stores the calibration in a JSON file shipped with the clock.
The program measures North by calculating the arctangent of the two magnetometer values that are in the horizontal plane. The atan2() function uses the signs of the values to determine what quadrant the vector is in and calculates the angle accordingly.
An accelerometer tells the program which way is down, and hence which two magnetometer axes are (more or less) horizontal. The clock is usually not accelerating. Or if it is, it will stop when it crashes to the floor. So it's sufficient to assume that down is along accelerometer axis reporting the largest absolute force, with the sign of the force indicating the direction.
The program has a table that specifies, for each of the six possible clock orientations,
which magnetometer axes to use, which axis values should be the atan2()
opposite and adjacent arguments,
and what angle to add to the result to yield the direction of North in a 360-degree circle.
AccelCoord = namedtuple('AccelCoord', 'key positive')
CompassAngle = namedtuple('CompassAngle', 'orientation opposite adjacent offset')
_ORIENTATIONS = {
AccelCoord(_KEY_X, True): CompassAngle(RIGHT_UP, _KEY_Y, _KEY_Z, -180),
AccelCoord(_KEY_X, False): CompassAngle(LEFT_UP, _KEY_Z, _KEY_Y, 90),
AccelCoord(_KEY_Y, True): CompassAngle(BOTTOM_UP, _KEY_Z, _KEY_X, 90),
AccelCoord(_KEY_Y, False): CompassAngle(TOP_UP, _KEY_X, _KEY_Z, -180),
AccelCoord(_KEY_Z, True): CompassAngle(BACK_UP, _KEY_Y, _KEY_X, -90),
AccelCoord(_KEY_Z, False): CompassAngle(FACE_UP, _KEY_X, _KEY_Y, 0),
}
The sole porpose of all of the above is to determine which sides of the clock are east and west, for simulating the sun. Of course, if the clock is facing exactly east or west, it's a toss-up which direction to use. So that the clock doesn't constantly change its mind in the toss-up case, the program applies hysteresis by requiring the compass direction to change by at least 10 degrees before updating the east/west orientation. In additon, the program damps the reading displayed on the web page by averaging it over two seconds.
There many definitions of when the sun rises and sets, to suit various purposes. For example, we have civil, nautical, and astronomical dawn and dusk:

But for our purposes, the notions of blue hour and golden hour popular in photography are the most useful:

The handy Python astral package tells everything we need to know about the sun's position on a given day at a specified location. Using the times calculated by astral, the clock simulates sunrise as follows:
- Start with a night-sky backlight color.
- During the blue hour, fade the sunrise pixel from off to a golden sunrise color.
- During the golden hour, fade the pixel to a daylight yellow, fade the three edges representing the sky to light blue, and fade the bottom edge to blue-green.
Repeat the sequence in reverse for sunset.
Where'd I get the golden sunrise color? When I was working on the software, I happened to be out driving during a spectacular sunrise. I took a photo and grabbed the color from it:

The program calculates the sun's position around the clock perimeter by assuming that the sun is traversing a half-cirle arc anchored by the bottom corners of the clock face. The sun starts the day at angle zero and ends the day at π radians, or 180 degrees:

Using trigonometry, the program can calculate which pixel to illuminate. There are four cases, labelled a through d in the diagram above; the math is slightly different for each. Here is the calculation for case a:

In actuality, the program pre-calculates the sun angle of each perimeter pixel. Then, given the current angle, the program does a binary search to find the closest pixel.
The clock phones home periodically, to report its health and to download software updates. I came up with a simple serverless design using an AWS S3 bucket. Each clock has a unique IAM User account that has permission to access the bucket, using credentials that are stored on the clock.
Once a day, the
send-log
cron daemon
uses the systemd journalctl
command to read
the clock service log and uploads the log to the S3 bucket. My plan is
to write an AWS lambda function to check the logs and report when a
clock is reporting errors or failing to upload the log.
Once an hour, the get-update daemon checks S3 for a new software update tar file. The S3 filenames include a timestamp, which the daemon records as it does updates, to know when a new file is available. The daemon downloads the file, verifies it contents by comparing a hash of the file's contents with the S3 etag, and then untars the file.
The daemon expects to find an update
script in the untarred files, which it simply runs.
The script can do anything, including installing
new clock software and changing other configuration. Of course, the
script can also have a bug that causes it to turn the clock into
a lovely blank modernist sculpture. If the worst were to happen, with some
effort I could work with my friend to remotely connect to a computer on his
network and ssh into the Pi from there. I considered, but rejected,
making it possible to ssh directly to the clock remotely.
Here are few more features it might be fun to implement:
-
Random phrase mode. Periodically display a phrase chosen from three random words. I imagine the effect would be as if the clock was composing poetry. Beth finds the idea creepy.
-
Track the position of the moon at night.
-
An option to turn the display off at certain times during the day.