Hexagons are cool -- they have six sides, they tile a plane, they have a structure like the carbon atoms in graphite, each cell has six neighbors, and there are six cardinal directions. But mostly, they are not square when so many computer things are. So I wanted to make a project using a display made of hexagonal pixels (hexels). I wanted the display to do something useful in addition to looking interesting, so I made it a clock.
The clock has 13 columns with either four or five LEDs, a total of 58 hexels that mostly fills a 17"x10" picture frame. It runs standalone keeping time via a battery-backup real-time clock, or can be connected via USB to a host computer that can push bitmaps, change color schemes, or most importantly set the internal clock. It definitely has some quirks, but I have learned to enjoy them.
Just for fun, it makes a transition at the end of each minute, which is sometimes an appreciation of John Conway's "Game of Life". For more information on Conway, see this article in the NYTimes or enjoy this cartoon by XKCD.
- Addressable LED strip (Adafruit NeoPixels, 2 meters of 30 pixels per meter, https://www.adafruit.com/product/1460)
- Microcontroller (Adafruit Metro Mini 328, https://www.adafruit.com/product/2590)
- Shadow-box picture frame. I used a 17"x10" picture frame meant to display three 4"x6" photos. The dimensions are the area of the glass. The frame needs some distance (~1/2" for my frame) between the glass and back plate. I used "Gallery Collage Photo Frame 3 Image 4''x6'' Black" frame that I got at JoAnn Fabrics (https://www.joann.com/gallery-collage-photo-frame-3-image-4x6-black/16619298.html)
- Smoke-tinted window cling film (I used Gila Smoke Glare Control film) -- or smoked plexiglas
- Battery-backup real time clock (a DS3231 module I got on Amazon)
- (optional) Light sensor (Adafruit VEML7700 Lux Sensor, https://www.adafruit.com/product/4162)
- 100 microfarad (or larger) electrolytic capacitor
- 220 ohm Resistor
- Wires, solder, soldering iron
- USB cord for power/communications
Step 1: Plan and Make a Font
The basic plan was to make a hexagonal grid of NeoPixel LEDs and then display the time on it. I found an INCREDIBLE website describing hexagonal coordinate systems (https://www.redblobgames.com/grids/hexagons/) that really helped with the design, and we'll refer to this site often. If you want to play hexagonal life, there is also a nice interactive site at https://arunarjunakani.github.io/HexagonalGameOfLife/.
I chose to use a "flat topped" grid because I could make a font that was fairly readable, but maintained a significantly hexagonal look. A couple digits are a bit of a stretch (4, 5, and 7), but you get used to them. I also found I could make all hexadecimal digits (0..F), which I thought was in agreement with the hex idea, so I adopted this font.
The font uses 13 hexels, three columns, and 4/5/4 rows. I encoded this font into a bitmap by using 16-bit unsigned integers and created bitmaps for each digit, which are in the Arduino code that you can download from the GitHub link.
Step 2: Make the Grid and Coordinates
After making the font decision, I needed to figure out how big the grid would be. In addition to the three-column-wide digit, we need an extra column for the space between digits, so that means four columns per digit and we need three full digits for the clock (12 columns), and then a leading 1 digit, which would be made of a single four-high row. These choices led to a grid that is 13 columns wide, and has alternately four or five hexels in the column, which requires 58 hexels, two fewer than the number of LEDs in the 60-NeoPixel strip. One could also choose to make a true four digit display, which would require 67 hexels and would let you display 24-hour time.
To address the display, I chose "axial coordinates" -- see the coordinates link, scroll down to axial and select "flat-topped". The two coordinates are q for the columns and r for the other coordinate, which increases to the right, but moving downward also -- really, go to that website; it is so cool. In this coordinate system, the memory storage is a bit inefficient, requiring 13 values of q and 10 values of r, but using 13 bits encoded in two 8-bit characters meant that 20 characters can hold the full bitmap, which is pretty small. The origin of the coordinate system is the center of possible symmetric forms, which makes it easier to check for symmetry (more on this later).
Step 3: Figure Out the Dimensions and Design the Circuit
NeoPixel strips are very cool for making linear structures, but of course a hexagonal array has many lines, so I needed to choose a direction. I wanted my display to be as big as possible to show the hexagonal nature, so I chose the wide spacing (30 NeoPixels / meter) and chose to run them vertically, in either four or five LED segments that were wired in a serpentine fashion. On this grid, the vertical spacing of LEDs is square root of 3 times the hexagon edge dimension. The strip having 30 NeoPixels / meter means a pixel spacing of 1.31 inches, so the hexagon's edge is 1.31 inches / sqrt(3) = 0.758 inches The separation between the columns of the array is then 3/2 times the hexagon edge size = 1.14 inches. It got pretty involved ... so I decided to write code to print out the hexagonal grid. The code and pre-made templates are available on GitHub. The template grid is produced at 600 DPI, which can be printed at that resolution (be careful that you printer isn't re-scaling!) to make the correct shape for the array. My picture frame had a glass dimension of 17 inches x 10 inches, so the full-grid template is scaled to that size. Because not many of us have large-format printers, I also include a half-grid template that can be printed on a normal US 8.5"x11" page of paper. Both versions are in the "GridTemplates" folder on GitHub.
The figure shows the wiring diagram for a 6-hexel display as an example. The microcontroller is powered over USB, which proved to be enough to also power the NeoPixels. The two other parts are a light sensor (more on that later) and a battery-backup real-time clock that keeps the time, which are connected via the I2C bus. A full-scale version of the wiring diagram is also on GitHub. It would be easy to adapt to any other microcontroller, but be forewarned that NeoPixels want 5V power and 5V digital logic signals to operate reliably and modern microcontrollers often use 3.3V logic, which might not work reliably.
Step 4: Build It
The picture above shows my physical build. This version had a separate power supply for the NeoPixels, but it proved unnecessary, so was later eliminated. I measured the locations of the pixels onto the back of the picture frame and then hot glued them down to the backing board. I then wired all of the pixels and glued down the other parts. Note that the +5V power for the NeoPixels is fed in on one bus and the ground on the other bus, while the digital control signal snakes down and up. Once you wire it, use a multimeter to assure that there is not a direct short between the +5V and ground rails!
Step 5: When at First You Don't Succeed
Becky Stern suggested a number of ways to diffuse LEDs, and a sheet of white paper works pretty well, but the light from one LED also illuminated the neighboring hexagonal area, leading to a non-crisp display. I tried many solutions for diffusing the LEDs and outlining the hexagonal array, but most were pretty poor. I realized I needed a hexagonal shadow grid that would isolate the hexels from their neighbors. I built it by cutting out "V"-shaped bits of black paper that would form two edges of a hexagon when bent at 120 degrees. The other dimension of this "V" is the spacing between the glass and the back plane of the picture frame, 1/2" in my case. Thus, the strips are 1+1/2" x 1/2" and bent in the middle. I used E6000 glue to attach the black "V"s to a transparency film. It took two 8.5"x11" transparency films to make the full grid. I printed out my grid on a page of paper and then overlaid the printed grid with the transparency film, spread a line of glue along the grid lines and then placed the "V"s on edge into this glue. I found that if you put down lines of glue and waited a few minutes for it to get tacky, it was easier to get the "V"s to stick where you wanted. Once the transparency films have the "V"s glued to them, I removed the paper grid templates and added a simple white sheet of paper as a diffuser. If you had a 3-D printer, you might get better results with less hassle. Overall, it probably took a little over an hour to build (not counting other failures...).
Step 6: The Full Grid
The photo above is the fully assembled shadow grid. As can be seen, light can leak through the gaps in the grid, which was a bit of an annoyance, so I glued on extra "V"s of paper over the worst of them. In the end, I think some of the light leakage give a more organic look to the clock, so I left some of them. Maybe I'll later fix them.
Step 7: The Final Product
Even with the shadow grid, I just couldn't get the hexels to look good until I saw in some videos in which people were using smoked plexiglas instead of clear glass on the front. I think it works because light coming from outside the box gets absorbed on the way in and out, so little re-emerges and it shows up as black, but the LEDs are quite bright and only pass the smokey film once, so they can be seen well. I was not able to get smoked plexiglas, but could find "shade control" window film at the hardware store, which worked really quite well. The photo in the header shows a blow-up of some of the hexels. You can see the E6000 glue acting as a bit of a lens, which was not desired, but seems OK and again lends an home-made flavor. I think that if you spray painted diffusing paint on the inside of the shadow grid grille, it might remove this effect but you don't really notice at a reasonable distance.
To review the hardware setup, from back to front: The back plane of the shadow-box frame is used to hold the LED array and electronics. The hexagonal shadow grid grille lies in front of the LEDs to allow the LED light to spread across the hexel. In front of the grille's transparency film, there is a sheet of white paper to diffuse the light, then the glass from the picture frame and last, on the outside, the "smoke" colored shade control film. I think the film could also be used on the inside of the glass, but I had a few bubbles which are less visible with the film on the outside, so I went with that.
Step 8: Write Code for the Clock
The code for the clock is available at the GitHub link. Please feel free to just use it or adapt it. The microcontroller I chose is the equivalent of an Arduino Uno, so I told the Arduino app that the "board" was an Arduino Uno, and I then selected its port and uploaded the code to it. Please see online help documents about how to upload the code and/or get the libraries (RTClib, FastLED, and possibly Adafruit_VEML7700 for the light sensor).
For those who are interested, some ideas from the code are below.
The basic data to be displayed is held in the "hexgrid", which is a bitmap where 13 values of the column index, q, are held in a 16-bit "word", and the 10 values of the "skewed row" index, r, are the index of the hexgrid array of words. The current hexgrid is displayed, but a prior hexgrid (last) is also kept for various calculations.
The NeoPixels are controlled by the excellent "FastLED" LED animation library, which requires a 58-element array of RGB colors to be displayed. To convert between the hexgrid and the pixel color values, we "walk" the hexgrid along the path that the NeoPixel data wire took in hardware. This path starts at the top left, goes down the first row (q is held constant, r is incremented) for four hexels, then r is held constant and q is incremented, which puts you in the lowest hexel of the next column. The index r is then decremented for five hexels, and then the process repeats. The "walk" algorithm is used for other purposes besides coloring, thus its uses are:
- Walking to "color": In this walk, the hexgrid bitmap is colored using various methods, but for any location on the grid, there will always only be an "on" or "off" color that is controlled by the bitmap's data.
- Walking for "Conway": Remember that the hexgrid is 13x10 = 130 possible values of q and r, but only 58 of these are on the display, so we can save some computational time by only calculating transitions for the displayable hexels.
- Walking for "symmetry": Here we check if the displayed hexgrid possesses inversion, vertical mirror (left/right), or horizontal mirror (top/bottom) symmetry, and if it does, the code always will choose the Conway transition. To check for symmetry, we iteratively examine symmetric points on the hexgrid to see if they match.
The color modes are:
- Mono: One foreground and one background in all locations
- Two-tone: Two different foregrounds, depending on the column of the display. This is used to distinguish between the hours and minutes
- Huewave: This mode has the color follow a hue wave that oscillates around the current foreground hue.
Between the minutes, a randomly selected transition is carried out for the last few seconds of the prior minute. As indicated earlier, if the pattern is symmetric, the Conway transition is always chosen. The current transition modes are:
- Conway: Plays Conway's game of life on a hexagonal grid with the rules that any cell with two neighbors alive will either remain alive or be born. Cells with fewer or more than two neighbors die.
- Swipeoff: Translates the hexgrid in one of the six directions of the hexagonal grid. The direction is randomly selected.
Please feel free to modify the code and make more interesting transitions and/or color schemes.
The real-time clock also has a temperature readout, so I also have the display show the temperature, which is accessed by blocking the light sensor (wave your hand in front of it) in the top middle of the picture frame. The temperature is then shown in °C and °F. It is funny to me that C and F were in our hexadecimal font set, and even look OK (unlike b and d).
Step 9: Serial Interface -- the Backdoor
Although the clock works fine on its own, I wanted to give it more capability, so I used a serial interface on the USB to transfer data also. First off, this connection is helpful for setting the correct time, but it is also used to change the colors or behavior. The commands are quite basic one-letter codes that are generally preceded by a numeric argument (represented by # in these comments). You can use the Arduino "Serial Monitor" to test out this interface (make sure to use 9600 baud). The most useful command sets the real-time clock:
- ####S = Set time to HHMM
For example, type "1255S" to set the time to 12:55. You can also decide on a color scheme you want to use. The standard color scheme is to display the minutes in the selected hue at full saturation and to display the hours in the same hue but only at 1/3 saturation (e.g., more white light than colored). The background is the opposite hue at low intensity. The relevant commands are:
- D = Set default colors (no argument)
- ###H = Set hue for display pattern (0...255) using FastLED spectrum HSV colorspace
- ###I = Set "inverse" hue for display pattern (0...255) -- typically horribly ugly and barely legible.
Any choice of the default hue you make will be retained until the USB power is cycled or the Arduino is reset, at which point the display will return to the default hue. The default hue is hard coded in the line #define DEFAULT_HUE, so you can change the value on this line and then upload the code to make the change permanent.
Other commands check information or play Conway at will:
V or ? = Show version, time, and temperature
Y = check the currently displayed hexgrid for symmetry (prints out to serial port)
####C = Enter Conway Mode for argument milliseconds
#W = Enter "swipeoff" mode in the argument direction, which has values from 0 (up), 1 (right and up) ... to 5 (left and up).
If you plug the clock's USB cable into a computer (I use a Raspberry Pi), the OS will assign it a port (e.g., /dev/ttyUSB0). I then use a terminal emulator (e.g., minicom, screen, or hyperterminal on windows and terminal on MacOS) to communicate with the clock directly using this port. The settings are 9600 baud, 8 data bits, no parity, 1 stop bit (9600 8N1). In this mode, you don't need to type enter, just type numbers for the argument and then a single letter for the command and it will happen. If you mess up, just type a random letter and start again. The display always reverts to the time, so you won't mess it up for too long. The serial port also gives you some feedback, which can be useful for debugging.
Step 10: Play With the Clock -- Have Fun
With the serial interface, you can play with the colors, which results in a few decent combinations and many terrible results as evident above, but you might find one you like.
The most interesting command is X, which allows you to transfer bitmaps directly on to the display. It is for advanced users because it generally requires that you access the serial port with some coding language like Python. The "X" command has a different syntax. It takes a pre-argument, which is how long to hold this pattern (in milliseconds), but then after typing the X, you need to pass twenty characters that contain the hexgrid to be displayed. These 20 characters are read without any filtering, so the normal commands won't work for the next 20 characters or the timeout happens. The timeout is set by #define XFER_TIMEOUT and is 500 milliseconds by default. If the 20 characters are not received, the buffer is cleared and normal command mode resumes. The command syntax is:
- ####X = Transfer binary hexgrid and hold for argument milliseconds
The 20-character array is made up of ten rows (r index) of 16 bit words (two 8-bit characters) encoding the q index. These characters are passed in MSB, LSB order, followed by the next row. To test, a novice can paste in a full X-command string (e.g. 5000XZZZZZZZZZZZZZZZZZZZZ) to the Arduino serial monitor or a terminal emulator, but the patterns made by type-able characters is typically pretty random. Since more interesting patterns require characters that are unprintable and the whole pattern needs to be sent faster than you can type, you really need to write code to push over bitmaps. I include some "helper" python code (fireworks.py or showppm.py) that uses the X command to show fireworks or the phrase "PPM" as examples. I calculate that at 9600 baud, the transferred hexgrids could go as fast as 60 Hz, so it could do some fast changes. I found that making the serial port's baud rate faster sometimes led to failures in transferring the data, which I think might be a conflict with FastLED, but that 9600 baud seemed reliable. If anybody gets interested in this project, it is my hope that the direct transfer could lead to interesting applications such as displaying CO2 mixing ratios (in PPM) from some another sensor or an online data source. One could also make a direct transfer mode that would transfer the color RGB values for the 58 hexels and use the device as a very tiny color monitor or do something more interesting.
I hope you enjoyed this project.
Runner Up in the
Make it Glow Contest
2 years ago
Another source of diffuser material is from broken LCD or LED monitors or TVs, it doesn't matter if the screen is cracked. There is diffuser material as well as Fresnel and clear plastic sheets.
When disassembling the older monitors and TVs, be careful of the thin glass fluorescent tubes as they have mercury in them.
Reply 2 years ago
Great idea for the diffuser. Thanks.
2 years ago on Step 10
Amazing Project ..
Reply 2 years ago
Thanks! Glad you enjoyed it.
2 years ago
If you look at the hexes flat side at the top, you can see how each run horizontally makes a line. That line goes: Flat, angle down, flat, angle up. Sort of like this: _/ \_/ \_/ Each side of a hexagon is the same length, so if your hex side is 1 inch you take a strip of paper and fold it every inch. Then glue together where a bottom flat meets the top flap of the next row. Now you can just carefully stretch the resulting net and fix it to the border. Did that make sense? I don't know how to get a character that prints a horizontal line at the top of the text line, so here's the best I can do for a drawing:
_/ \_/ \_/ \_
\_/ \_/ \_/
Reply 2 years ago
Thanks for the good idea. Unfortunately, I tried and couldn't get it to work. I was able to make a hexagonal grid this way, but the spacing of the hexagons was not very regular. The hexagon grid center-to-center spacing needs to match the LED strip distance between LEDs, so (discussed above) the edge of the hexagons needs to be 0.758 inches. I tried to mark the paper at this distance and then pre-fold, but I found I was not accurate enough and over the width of the array the spacing got off. Possibly if one worked more carefully to lay out the fold lines you could get it to work.
Thanks for the comment. The making of the shadow grille turned out to be one of the hardest parts of the project. Let me know if you try it!
Reply 2 years ago
It's likely an error of some sort in my instructions. Apologies. I also do a lot of papercraft and so use some fairly solid tools and techniques in the construction of my grids, and that might make the difference. I also made a spacing jig to hold the hexes to the proper width for gluing down. I am not going to make the clock exactly, I am going to use your techniques to create a display for a project of mine. This is perfect because I'll have quite a few extra addressable LED's left over and a blank space that needs filling in the front. Your font will look sweet scrolling messages as well. :D
Reply 2 years ago
It would be great to hear about your experience with the technique. If you can be pretty accurate with the papercraft, I think that it can work to have more of a complete hexagon made automatically. I found that hexagonal core packing materials use these type of techniques. For example, this site (http://www.lhexagone.com/en/cardboard-honeycomb.php) has some pretty solid looking cardboard honeycomb. For the application with LED strips, however, you need the pitch of the hexagons to match that of the LED strip, so I could not find a commercial product that worked. Have fun with the scrolling messages!
2 years ago
Amazing work! I'll absolutely try that. Maybe you are interested in IT, so you can visit here https://duzce.edu.tr/yonetim-bilisim-sistemleri/Sayfa/ec9a/yonetim-bilisim-sistemleri-nedir
2 years ago
Outstanding work, Bill! Everyone's emailing me about this project, and they don't even know we're cousins. 😁
I'm putting this one on my 2021 build list for sure. A future family heirloom!
Reply 2 years ago
Thanks for the nice words, cousin! It would be super cool if it became an heirloom. You could try 3D printing the grille -- I would have if I had a printer.
2 years ago
Awesome work, Bill! Thank you for posting such a detailed guide!
Reply 2 years ago
Thanks! The tricks to diffuse LEDs made it look tons better.
2 years ago
User ravijag is considering building a clock with a higher LED density (60 LED/meter), which I think would be most easily done by making the clock half scale (about 9 inches wide). It might then be hard to build the shadow grille at that small scale. However, you could 3-D print the shadow mask. I built a 3-D model that could be scaled appropriately and printed. I am not very good with Tinkercad, so the scaling of this model may not be correct, please check it before you print. You can print this twice and then glue the two halves together to make the full shadow mask. Let me know if it works!
2 years ago
That looks fantastic!
Reply 2 years ago
2 years ago
Interesting you chose the colours of the blockbusters game show https://deadline.com/wp-content/uploads/2018/12/rexfeatures_618137rp.jpg
Reply 2 years ago
Wow! That is very cool. I had no idea of this game show. They used hexagons with the flat top also. The colors were chosen by the FastLED Hue-based colors. The yellow is the main color, and the white is a less-saturated version of the yellow and the blue/purple is opposite on the color wheel (and dim). I'm bad with colors so the FastLED colors helped to make it better. If you scroll down in the instructables, I have it in a few other schemes (at least one very ugly).
Reply 2 years ago
I think anyone from the UK would have thought the same thing as me when they saw the colour scheme :) Cool project
Reply 2 years ago
That gameshow looks cool. There apparently was also a hex grid board game that is noted on some of the wikipedia pages. Thanks very much for the link to that show.