In late 2014 I wanted to make a watch. Smartwatches were starting to pick up and get some press, but none of the ones on the market fit my needs. Really I just wanted a simple one with a bright, clear display. I suppose it's more of a plain old digital watch, actually, as it won't connect to my phone. [1]
I didn't make much progress besides getting the components then, it being my senior year of high school and all. But now (mid 2017), I'm getting back into it and really enjoying this project. It's great coming back to it with a greater understanding of nearly every aspect of this project.
The primary hardware components I have are two Adafruit Trinkets, two Pro Trinkets (Hackaday limited edition :D), one 14-segment quad-alphanumeric display, and two OLED displays (one SPI, one I2C).
This is the primary sketch. While the 14-segment display is nice, it is rather thick. The OLED display is fantastically thin and great for this type of wearable project. It also uses considerably less power. The current sketch supports showing the time (obviously), nicely formatted hours (i.e. 12 instead of 0 for midnight/noon), a blinking colon separator (once a second), and an AM/PM display. You can also use a single button with different timed press sequences to set the time. You can find a video of me setting the time here. The timer and interrupt code would need to be adapted for the Trinket, but the Pro Trinket's chip (ATmega328) is more similar to that of the Arduino Micro (ATmega32U4), perfect! Note that the capacitor seen in the above media is not necessary as there is software debouncing.
The Trinket is unable to use the Adafruit_SSD1306 library (and thereby the Adafruit-GFX-Library) as-is. The OLED display is 128 pixels wide by 64 pixels tall. The Trinket would need 1024 bytes to buffer the display (8192 pixels / 8 bits per byte = 1024 bytes), but the Trinket has only 512 bytes of SRAM. Instead of buffering and writing the entire display, we can write raw data and commands. That is what this example does.
Currently it sends 32 commands as the initialization sequence (various configurations, see Adafruit_SSD1306::begin
for more detail). Then, it fills the screen with a pattern akin to Sierpinski's Triangle. Finally, it demonstrates specifying an area and drawing only to it.
When I wanted to try idea 2 of #4, the Trinket wouldn't enter the bootloader, it would just stay at a solid red light. I ended up finding my old Arduino Mega and using it to repair the bootloader. The trinketloader
sketch is from Adafruit. I modified one line to force it to recognize my Trinket. I was able to recover it after removing the jumper from the Arduino's 5V to the Trinket's VBAT+ and powering the Trinket via microUSB. However, after soldering on breadboard headers and playing with it a bit, it's stuck once again and I can't recover it.
This is a highly minimized version of the ssd1306_128x64_i2c example included with the Adafruit_SSD1306 library. It's basically just the "text display tests" section.
By default, the quad-alphanumeric display is at brightness 15. When I ran my brightness test, it lasted 6 hours. Lowering the brightness to 0 made it last 9 hours on a 105mAh battery. Note brightness 15 is, in fact, very bright.
Simple enough, this sketch shows each of the 14 segments (and digit separators) and their indices. Given the Adafruit_AlphaNum4::writeDigitAscii
method this knowledge shouldn't be necessary. Note you can turn on the digits separator by appending a boolean argument of true
, i.e.
alpha4.writeDigitAscii(1, '3', true);
alpha4.writeDigitAscii(2, '1');
alpha4.writeDigitAscii(3, '4');
The 14-segment display uses a lot of power. Much more so than the OLED display. To help mitigate this, we can make the HT16K33 LED controller enter standby mode. I still need to run a battery test but this should save a ton of power.
On the Arduino Micro I couldn't wake the LED controller directly from the interrupt service routine. This is because the I2C transaction takes some time. When I did this in the ISR the Arduino locked up (I couldn't upload code to it without manually pressing the reset button). Instead I set the wakeup
flag to true
, which should be almost instant, and handle that in the loop()
method. This does work.
I figured out how to do this from the datasheet. The System Setup Register has a single significant bit which "Defines internal system oscillator [state]" where "{0}:Turn off System oscillator (standby mode)" and "{1}:Turn on System oscillator (normal operation mode)." I was able to verify this in the Adafruit LED Backpack library, where it writes 0x21
to turn on the oscillator. What's interesting here is that both the command and data are in this one byte. The first (most-significant) nibble is 2h (hex), which matches 0010b (binary) as specified in the datasheet. The low nibble is 1h for turning the oscillator on, but sending the data 0h would turn it off.
This sketch turns the built-in LED on for half a second, then off for half a second. It uses the hardware timer. Although it's not even a dozen significant lines of code, it's a minimal complete example.
Maybe I could use a Feather with an ESP8266 to make a real "smart" watch. I was actually thinking of getting a basic Feather now and snapping off the proto space. This would take up less space than the Pro Trinket and LiIon/LiPoly Backpack, as the Feather has built-in charging. However, this would be another $20 and I want to make something using what I have, even if it might end up slightly larger.
Pinouts: Trinket (note pins 4/5 are used for USB programming), Pro Trinket, LiPoly/LiIon Backpack.