Ever since I’ve known Max, I have realized that there is no keeping up with him or even beating him — that is until now when he challenged me to a design-off contest over who could build the best pixel ring clock. So I thought to give it a whirl.
This is a work in progress. Comments are welcome.
I thought my clock’s display should be quite active when running, understand what I say to it, have capacitive-sense touch buttons that also would know when it was touched anywhere, and have a speech and audio playback synthesizer to complete the communication-to-human part of the beast. Lastly and obviously, no clock would be complete without a time-keeping mechanism, so a clock/calendar is included.
I ended up with the following for hardware and will give my reasoning:
- Arduino Leonardo . The UNO and Mega have a USB to TTL chip that connects to the hardware serial port, which ties up the hardware serial port that I want to use to talk to the voice recognition system. The alternative is using the software serial interface, which might work if changed to be interrupt driven – however, my clock is going to be too busy to have to take on this overhead. Available from SparkFun, the Leonardo has the USB built into the processor, which frees up the hardware serial port. It also makes the ‘bridge’ function to connect the PC to the voice system simple and easier to share with the debug-serial I/O console built into the compiler environment, not to mention that it also runs 115k baud with minimal overhead.
- ChronoDot clock/calendar , available from Adafruit, was almost a no-brainer. It interfaces to the I2C wire bus, is very low power, and keeps outstandingly accurate time. Also it comes with the obvious other things required, such as the button-cell battery, for example.
- EasyVR shield V3 , from Veear and available from Sparkfun. This little beauty is both a speech recognition engine and a sound player in one. Having extensive support and library, this board-shield offloads the Arduino from what would otherwise be an impossible task for the 16 MHz memory-limited processor. The hardware can be run in standalone PC mode, as well as ride on the Arduino, to make it easier to develop an operating environment. Its separate commander software that runs on the PC makes the job of teaching the device words (speaker dependent) and then downloading them a breeze. For Arduino code development, there are very good examples to use. Lastly, for speaker-independent additional-word supplements, a software tool can be purchased.
- Groove Q-touch sensor , from EpicTinker, has the following features: Shares the I2C bus nicely, has 7 separate very noise-tolerant capacitive inputs, self calibrates, and is very small itself. For debug, three of the buttons can optionally be used already configured on the board’s underside.
- The concentric, three pixel rings , which are 60 pixels for the outside-most ring, 24-pixel ring for the middle, and a 12-ring for the inside-most ring, make a tasty clock. This makes the minutes and seconds share the outside ring, the 24-ring for hours when in the 24-hour mode, and lastly the 12-ring to display the hours in 12-hour mode. Having the extra unused ring depending on the mode, allows it to be used for AM/PM indication, and status display of other clock functions such as voice, for example.
- Power supply . An external power supply is required, since all-white/all-on and fully-bright pixels draw considerable amps. I chose the Recom Power RAC60-05S/OF from Digi-key. It has room to grow at 10 amps, is relatively small, and is only about $21. One down-side note to mention however, is that whenever the supply draws more than a few amps, it ‘sings’ a bit, which could get a tad annoying if it turns out to be loud enough after mounting it inside the clock.
To get started:
I hooked everything up as mentioned, and used digital pins 4, 7, and 8 for the 60-ring, 24-ring, and 12-ring respectively. I left the pullup resistors for the I2C bus installed on the Q-touch, because I changed the I2C library away from using the standard WIRE library, and the replacement didn’t enable the internal pullups. I will discuss this library replacement later.
At first it seemed logical to try to use everything stock and standard, as supplied by the manufacturers of each of the respective pieces, to save time and keep it simple, clean, and understandable. However, it quickly became apparent that to have a dynamic real-time display, something like an interrupt-driven task manager would make things work better. The manager would not need to be a full up pre-emptive multitasker operating system, but could be a single-level interrupting scheduler. Hunting around for exactly that on the web, I landed on something that was an outstanding multitasker but wasn’t interrupt driven. To overcome this problem, I thought to give it a try as a quick poll in loop(), then add the interrupts as necessary. To its credit, this library is very full featured, having things like run once, run every time on a time, run a certain number of times, oscillate, pulse, stop, update, and find free event index. I found the
t.every( xMilliseconds, routineName, number-of-times)
quite useful, where it calls the routinenamed every x milliseconds for number-of-times and then stops.
All tasks are sampled to see if the timers are to be triggered for calls, each time the t.update() is run. As nice as this was to use, it had the down-side that PWM, millis() and other internal timer delays, as well as other timer library functions were not useable. So rather than expend energy to add interrupts, I thought to look for another solution that was already interrupt-driven, which led me to LeOS (little embedded Operating System). While this was not as full featured, and lacked the run number-of-times item I wanted, it did have everything else, including interrupts, task restart, task change, etc. Much later on I would also discover, to my chagrin, that this scheduler was not compatible with the NeoPixel and WIRE I2C libraries, causing me to hunt/replace these with something that would work. To these ends I first found the Neopixel replacement, light_WS2812 which worked well enough, even though it was a very basic engine. Next I found the replacement for the I2C which allowed to disable interrupts, set speed, and other nice things, but which mainly was compatible with LeOS. I did notice that this library didn’t enable the internal pullups, so I went to make sure there were external ones provided on at least one of the boards connected to the bus, rather than spend the time to find the software fix. After substituting appropriately this softI2C master to the function calls to the ChronoDot, I thought to tackle the touch controller, which I had working originally using the Wire library, prior to integrating LeOS. This was not to be.
At this point, I had a beautifully spinning and whirring display, eloquently running, voice recognition accepting a trigger-speech command, and responding with speaking the current time. However, I couldn’t get the I2C library swapped out for the WIRE library on the calls to the touch controller. It seems that the trouble lies in the compiler’s insistence that the I2C be declared in proper scope inside the touch controller’s library, where the I2C is itself a library. All the techniques I usually use to resolve such issues, such as pulling the code out of the library up to the top-level sketch, for example, have failed.
Some of you who read this, may have discovered a few OTHER quirks with the otherwise nice compiler. As an example that I ran across on this project, I had a function called serialIN(), which worked just fine as the code steadily grew, but then suddenly the compiler began complaining that serialIN was not defined in this scope. I worked for hours trying to fix this, until I accidentally renamed it to iserialIN(), which made the compiler happy! Now mind you, the serialIN was not a keyword (Serial.xxx with the capitol ‘S’ is a keyword, as it would indicate this by a change of text color). There were no constructors, classes or anything else that I could find, having been added, to cause the error. Not having much more patience to bear, I left it like this and decided to move on.
To solve this last seemingly insurmountable problem (to me) with the I2C library replacement for the touch controller, I welcome any ‘C’ guru to take a look at this and offer a suggestion/fix. I will supply the appropriate code snippets for all who brave forward.
By the way, I have included a video of what I have so far, for you who are interested to drool over, or cry, as appropriate.
Thanks in advance!