Hey Hef, ask me what time it is.
What the hell?
Right? This is an idea I had at some point along my four day drive across the country. QR codes are very popular for some reason. Marketers insist that they're useful and demand to put them in everything despite the fact that nobody uses them.
The real problem with QR codes is that they force people to use a machine to translate what could easily be human-readable information. Rather than a QR code, why not just show the text "Chevy.com" or even better just the word "Chevy" because everyone knows how to google stuff? Seriously, I've seen QR codes on highway billboard signs as if anyone is going to whip out their smartphone while they're pulling 65.
So why a clock? As I've said before, clocks have already sort of been perfected. A digital wristwatch is probably the most convenient, accurate, and durable way to tell the time possible. Any attempt to change the typical blue-light special wristwatch will only make it more expensive or harder to read. This clock attempts to do both.
Also, this clock points out the ultimate irony of QR codes which is that they are a technological convenience that really isn't convenient. In order to read this clock, the user will require some sort of QR scanning device which is guaranteed to have a time-telling function built into it already.
So, that's why I made it. Hilarious, right?
The goal here was to build an attractive self-contained desk or wall clock that can display the current time as a QR code. This presented a challenge as there are 1440 minutes in a day, and it would take a substantial amount of memory to store that many QR codes. Because of this, my device had to generate its own QR codes on the fly.
Despite this project being the most intricate circuit board I've made so far, it probably has one of the most straight forward schematics.
I know it looks pretty complicated, but it's really just a bunch of repitition.
This project was my largest PCB layout to date (unprofessionally, of course). It was so large in fact that it exceeded the limitations of the free version of Eagle. I ended up purchasing a license for the full version to finish the job.
Given how incredibly busy and large this board is, I decided it was an appropriate occasion to get the board properly manufactured. In the past, I've used Advanced Circuits which is a fab house based in the 'States. Their turnaround is fast and reliable, but their prices are a little high.
This time I chose Myro, a Chinese-based fab house. Their customer service was quick and competent, and they even managed to squeeze in an update to the silkscreen labeling that I sent them a day after the original order.
The setup fee ran me $73, but each board after that was only $4.11, so I ordered 5 to be safe. They are beautiful:
The whole bill with shipping ran me $128.55.
I was a little surprised when I got the boards (although I shouldn't have been) that all of my vias were exposed:
Typically vias that only connect a trace on one layer to another layer are covered up with solder mask (the black stuff). This keeps them protected and prevents possible accidental shorts. With the default settings, Eagle chooses to leave all of the vias uncovered. Although it ended up being a convenience for this PCB (see below), I'll have to make sure to change the settings next time I order a board.
The biggest issue with tis project was the sheer number of LEDs required. As I'll explain below, I needed a 21X21 pixel array with a 1 pixel border around it. My original thought was to align individual LEDs in a grid and create a custom mask to go over them and turn them into pixels. I even went so far as to design this mask in google sketch up:
It wasn't until after I designed this piece (and even found a laser cutter willing to help) that I remembered that LEDs come pre-packaged in matrices. The only issue is that I've only ever seen circular LEDs in matrices. Looking through the Digikey stock, that's all they seemed to have too. I was concerned that a typical QR code reader wouldn't be able to understand a grid of circular pixels, so I submitted a post on reddit to find out..
Although the replies were mostly positive, I dropped the whole concept when I found a square LED matrix on Adafruit. This is the same one I used for this project. In fact, that project was simply a way of vetting the display before I dumped some cash on circuit boards. The display is a red-green LED matrix although I really only needed one color. For some reason, I was convinced at the time that simple white LED matrices didn't exist and decided to use the R-G display with both colors tied together. This gave me a nice amber color which is closeish to white.
I found out later that white LED square-pixel matrices do exist and are actually a lot cheaper. By the time I discovered this though, I had already ordered my PCBs and reasoned that it would be more expensive to re-spin the PCBs and use cheaper LEDs than to simply use the more expensive LEDs.
Each matrix is 8x8 pixels, so with nine of them, I was able to create a 24x24 pixel matrix. The QR code only required 21x21 pixels with a 1 pixel border which left me with a single row and single column unused. Although it's slightly unsightly, the extra row and column of "dead" pixels ended up coming in handy when I accidentally burned out an LED that just happened to reside in the dead space.
One interesting potential problem with large arrays of LEDs is color consistency. From day to day, the chemical make up of LEDs leaving a factory can vary slightly due to imprecision of equipment or contaminants. What this means is that the color output of a particular type of LED can vary significantly from day to day or from factory to factory.
When my PCBs first arrived, I only had the single LED matrix that I had previously ordered to play with. I quickly soldered it in and got a head start on my firmware before the other displays arrived. When they finally arrived, I soldered them in place only to notice that something was wrong:
It's really subtle, but if the color on your monitor is accurate enough, you might notice that the LEDs in the top left corner are sliiightly lighter than the rest. Although it was difficult to capture it on camera, this issue was very pronounced in person.
I totally anticipated this being a problem (which is why I ordered extra displays), but what I wasn't anticipating was the difficulty I had in removing the offending display. Because they were so tightly spaced, I ended up having to...well...
Fortunately, the PCB only suffered minor damage.
Much better. I was pleased to see that I got the spacing just right on the LED matrices so there was no visible gap or separation between individual 8x8 displays.
In a perfect world, I would have driven each LED individually and used a shift register to store their values. Although this would have been pretty expensive (55 shift registers), it would have allowed for DC LED operation and would have provided a frame-buffer that resides entirely off the micro controller. Basically, once the LEDs were written for a frame, they could be completely ignored until the next frame was ready.
Unfortunately, this design was impossible due to the way the LED matrices were wired up. To save pins, the manufacturer multiplexed their connections. Rather than having an anode and cathode line for each LED, they had a cathode for each column, and an anode for each row. With this configuration, you are only able to light up a single row of LEDs at a time, but by jumping from row to row quickly enough, you can maintain the illusion of all of the LEDs lighting at once.
The LEDs are rated for 30mA maximum DC current, but they can actually go a lot higher when operated with a low duty cycle. Because my multiplexing design would require each individual LED to be driven at a low duty cycle (12.5% at the highest), I considered allowing for a larger amount of current, but I ended up choosing resistor values that would only allow me to drive them at their maximum DC current. I was worried that during development I might find myself leaving the LEDs on for extended periods of time and burning them out.
To display a single row, PB0-PB2 are used to select one of the 8 cathode lines to pull to ground. At the same time, PD2, PD5, and PD7 are used to clock data out to the shift registers attached the the LED anodes. I chose to have three separate lines of shift registers to parallelize the operation and increase speed. With this setup, three rows are displayed at a time.
Dimming is controlled by the ~OE pin which is attached to all of the shift registers. When that pin goes high, all of the LEDs shut off. By toggling it high and low quickly, I was able to lower the apparent brightness of the LEDs. If I designed the circuit board again, I would have included a pull-up resistor on this pin. As is, there is a brief moment when the clock is first powered up where the pin is still low before the micro controller wakes up. The result is a very brief but very bright flash of random data on the LEDs. A pull up would ensure that the default state of the LEDs is "off".
The entire circuit runs off a 5V DC power adapter. Given that this display was designed to be plugged into the wall, I had some leeway with power sources and chose 5V for its convenience.
When I first built the clock, I had an issue where the clock would lose up to 1.5 hours over the span of 8 hours or so. I scratched my head over this problem for a while and kept checking and double checking my firmware looking for errors.
Out of ideas, I started poking around on the circuit and saw this on my 5V line:
What you're looking at is almost TWO VOLTS of swing on my 5V line. This fluctuation is due to the large current draw of the LEDs which is switched on and off rapidly to refresh the display and control brightness.
I remember the exact moment where I had completed the PCB layout and was ready to send it off to the fab house when I realized that I didn't have any bypass capacitors on my power rail. Exhausted and unwilling to go through the effort of creating a new electrolytic capacitor part for my library, I added C18 and C19 (small surface mount capacitors) and called it a day. These were definitely not adequate. This seems to happen to me a lot.
Perhaps unsurprisingly, this massive fluttering of the 5V rail was causing my clock crystal to act up and lose time. I was fortunate that there were some conveniently placed uncovered vias where I was able to squeeze a 470F capacitor without incident:
With this capacitor in place, my voltage rail calmed down substantially:
When I first started this project, I was using my old standby, the ATMega48. This is the same part I used in the DJ jacket, among other projects. What I soon realized after compiling my code is that the QR code generator was much too large to fit into the 4k of memory provided by the ATMega48. Fortunately, there are pin-compatible parts with larger program memories that can easily be swapped in for a situation like this.
To be safe, I upgraded all the way up to an ATMega328 which has 32K of program space even though I ended up only using 8k. I guess it was sort of silly to do development work on a 4k part when the 32k equivalent was not much more expensive. I guess I've never really had a need for that much memory before. I'll be sure to order larger parts in the future so I don't run into this problem.
The micro controller is set to run off its own internal oscillator, but it has an external oscillator for use in keeping up real time measurement. The values of C16 and C17 were determined using table 8-7 on page 32 of the datasheet.
As a side note, part of my schematic here is redundant. I accidentally tied the programming clock and shift register clock lines together by calling them the same thing. The end result is that the LEDs go nuts during programming. The only real issue is that I have two pins of my micro controller tied together, so I had to be careful to ensure that one of them was always configured as an input to prevent one output from trying to drive another.
That just about wraps up the hardware.
I'm sure the first question you have is "how did you generate those QR codes?". I will give you a somewhat satisfactory answer.
QR Code Generation
QR codes are damn weird. As mentioned above, there isn't enough memory on any AVR to store the 1440 QR codes that would be necessary to display an entire day's worth of minutes (around 600k with no compression). Because of this, I had to write a function that generates QR codes on its own.
QR codes are really weird. There is a lot of sophisticated math that went into the design of QR codes that makes them very resilient to noise. With the right settings, you can create QR codes that can have large portions cut out of them and still function. What this means is that the generation of a QR code is an incredibly complicated and convoluted process.
When I started out on this project, I had this vision of explaining to you the inner workings of QR codes. This quickly died when I realized what that would entail.
I ended up basing my generator off of the excellent tutorial that I found on Thonky.com. He walks through all of the steps required to generate a 21x21 pixel Version 1 alphanumeric QR code. This type of code can store 16 characters of text (all capitalized), numbers, and a few symbols. As it turns out, this is exactly what I needed for this project.
If you're planning on following along with that tutorial, keep in mind that when he says "ASCII" he actually means "Some weird coding scheme that only QR codes use", and he also has a slight error in his final output:
It's subtle. Mine is the one on the right (the correct one). Because of the built-in error correction, it's difficult to catch errors like this. Both of these will scan successfully.
Happy with the code I had made using this tutorial, I set about understanding QR codes in a broader context (different versions, different encoding options) and went straight to the source: ISO 18004.
This document is the official specification of how QR codes work. Unfortunately, writing specifications isn't free and this particular one costs about $225. If you are really interested, you can do what I did and find access to it at a local university's engineering library.
Thumbing through this spec, I found diagrams such as this one:
and decided that it was a waste of everyone's time to continue. If you are really interested in writing a QR code generator, I will give you the source code for the QR generation portion of my firmware:
When compiled and run in a terminal, this program will take up to a 16 character input (in all caps, that's all this type of QR code supports) and output a valid QR code along with a few of the steps along the way. Read the Thonky tutorial for details.
I will warn you that I am not a coder, and this code is incredibly ugly. A large portion of the QR generation process involves using a left-justified binary string and I found it easier to code a way to make that happen than to do the mental gymnastics of turning LSB into MSB. I'm sure it can be improved and optimized. I'm really not concerned with that.
It's still a good resource though as it will let you check each step of your QR code generator against a working one. One of the challenges I came across in making my generator is that it was difficult to see where a problem was as just about every QR code generator only shows you the final result. Thonky's tutorial has the steps laid out, but only for the input "HELLO WORLD". I found that for a while, that particular input worked with my generator, but a number of bugs were messing up other inputs.
For example, one bug made it so that the phrase "HI LAUREN " would encode a valid QR, but "HI LAUREN" would not. QR does a good job of scrambling the input, so two very similar inputs can produce very different outputs.
If you find one of these types of errors in my code, please let me know.
This (very unoptimized) QR generator takes approximately seven seconds to complete on the ATMega328 running at 8MHz (10 seconds if I'm refreshing the display at the same time). Fortunately, that's less than a minute, so I'm okay, but damn that is a long time for an AVR.
Everything that isn't QR related
If you read this blog often, you'll note that I try to do as few things in software as possible. Because the QR code generator sort of required a large chunk of software, I made sure to do just about everything else in hardware.
To keep my clock from being a distraction, I incorporated an automatic dimming feature. A phototransistor is used as a pull down on one of the ADC lines. This ADC value is read in on regular intervals (whenever the display is refreshed) and an optimal brightness is determined using a simple lookup table. To save time, I set the ADC to start its measurement at the end of a refresh operation, and collect the values at the beginning. In this way, it can do its busy work without taking valuable time away from the QR code generation in the main loop.
The brightness is controlled by flicking the ~OE pin high and low quickly and varying the duty cycle. The ATMega328 has an hardware PWM generator function which can be made to directly control an output pin (OC0A). I made sure to connect ~OE to this pin.
In my configuration, all I had to do was start up Timer0 in the appropriate mode and let it go. Timer0 starts from zero with the ~OE pin pulled low (LEDs on) and counts up. When its value equals the value stored in the OCR0A register, it pulls ~OE high and turns the LEDs off. When it gets to 255, it restarts. Without another single line of code, it maintains the duty cycle on the ~OE pin and maintains the display brightness. To change the brightness, I just had to change the value in the OCR0A register.
Timer1 handles refreshing the display. Originally, I was just going to run Timer1 in an overflow mode so that it operates on regular intervals. Unfortunately, TImer1 is a 16 bit timer, and a 16 bit timer takes too long to count up and overflow.
I ended up using an output compare mode. Every time Timer1 reaches the value stored in OCR1A, it refreshes the display. I can change the framerate by simply changing this register value.
Refreshing the display is a fairly complicated process as a function has to grab some values from a special frame buffer array, punch them out the shift registers, and switch PB0-2 to the appropriate values to select the right row.
I had an issue at one point where it turned out that there was a very brief period in time when the shift registers were updated, but PB0-2 were not. The results was a faint "ghosting" effect on the display. I ended up coding it to turn off the LEDs while this update is occurring so that they can all be turned on once the change is complete.
The 16 bit overflow delay thing did give me a funny idea though. If I really wanted to mess with people, I could lower the refresh rate to the point where digital cameras can't read it. That way, humans can see the detail of the clock, but can't translate it, and computers can translate it, but can't see it.
I crack me up.
Note: Despite what Hack-a-Day said, the final version of the firmware makes the clock perfectly readable by most QR code readers as demonstrated in the video below.
Timer2 on the ATMega328 has a special feature that allows it to be run asynchronously which means that it's actually run off a different clock from the rest of the micro controller's operations. The designer has the option to use an external square wave or an external crystal. In the latter mode, the pins are specifically tuned for operation with a 32.768kHz crystal. This frequency is very convenient for use in digital timekeeping systems as 2^15 ticks equals one second.
I set up Timer2 in an overflow mode which means that my interrupt code will run every 256 ticks. In addition to this, I set up Timer2 with a clk/256 prescaler which combined with the overflow means that it will only run its interrupt code every 65,535 ticks which at 32.768kHz works out to once every two seconds. Seeing how I don't need any granularity under 60 seconds, having the interrupt run only every two frees up a bunch of time for the all-important QR generator to do its magic.
Working with Timer2 in asynchronous mode is odd. Any attempt to write to a Timer2 register only takes effect at the next clock cycle which given how slow 32kHz is compared to the 8MHz core clock can take a "while". Because of this, the firmware provides you with flags that let you know when the registers have been written. You also have to be very careful as Timer2 registers can easily become corrupted if you do things out of order or try to put the micro controller to sleep.
When enough ticks have gone by to add up to a full minute, I have a small snippet of interrupt code that sets a flag. This flag gets picked up in the main loop and tells the program to output the currently stored QR code. Because it takes 7-10 seconds to generate each code, I have it generate it for the next minute at the start of the current minute and then hold on to it until the time is right.
Setting the Time
Because QR codes are unreadable, and the display takes 7-10 seconds to create a new QR code, I quickly realized that setting the time on this thing was going to be a real hassle.
My solution was to start up with a simple time setting mode. The time is displayed in a simple human-readable format on the screen and modified by hitting the the buttons. One increments minutes and the other hours.
Every time a button is pressed, Timer2 (and the Timer2 prescaler) is reset. This makes it so that the following minute starts at the exact moment the user presses a button. With some coordination, this allows the user to easily synchronize this clock with another one.
After 10 seconds of non-interaction, the QR time setting mode disappears, and the QR clock takes 10 seconds to generate its first code before displaying it. This time is only displayed for about 30 seconds though after which time it's been a minute since the last button was pressed and the clock updates.
I wanted to find a way to have it get a head start on the code while still in the time-setting mode, but I found it impossible (read: kind of hard) to abort the QR code generating process once it started.
Here's a video that hilights the QR clock's operation:
I've put a lot of time into this project and I am extremely pleased with how it turned out. I have a few ideas for the clock for the future. I made sure to include RX and TX pins on the PCB that will allow me to connect a QR clock to a PC and use it as an external QR display for a number of applications.
Given that it can already generate its own codes, a simple firmware update could also add a simple calendar function. "12:00AM 10/31/12" adds up to 16 characters which is exactly what this thing can display.
I had another idea for a more human-readable version that includes a pixelated analog clock in the center of a larger QR. This would take advantage of the error-correction capabilities of QR as a scanner wouldn't be bothered by the clock covering up a portion of its data (as if you would scan it after seeing the analog portion).
If you are interested in the QR clock let me know. Unlike the bullet counter, this clock doesn't require any specially machined parts, so mass producing them is easy. I might try to see if I can sell a bunch to raise money for a charity.
Project files can be found here: QR Clock package v1.0