Introduction: ATtiny Watch Core

This instructable show how to use an ATtiny85 to create a tiny watch core that can run over 1 year before recharge or replace battery.

The instruction to make the watch case may be in next instructable.

I have made a few Arduino watch before, but I found the watch using ATmega boards or ATmega chips are a little bit too big to wear. So I would like to make it with a smaller chips.

I have choose ATtiny85 just because I can easy to get one.

The challenges for using ATtiny85 are:

  1. It only have 8K flash memory
  2. It only have 5 IO pins (I would like to keep the 6th IO pin as reset for easy re-programming)
  3. Since the above 2 limitations, it is hard to find a complete source code for an ATtiny watch on the web

I design to develop a tailor-make code to complete these challenge, it include:

  1. Power control to make the watch can run over 1 year
  2. Revise a time library to utilize WDT for power saving purpose
  3. Trim down a display library to meet 8K binary limitation
  4. Customizable font type
  5. Basic UI for adjust time just like a normal digit watch

Step 1: Preparation


Today core subject, ATtiny85.


Any ISP that can program ATtiny85.


In general expectation, a watch should run over 1 year without charging or replace battery. For my simple measurement of power usage and the battery specification, CR1220 can only run about half an year, CR2016, CR2025 and CR2032 can run over 1 to 2 years representatively.


Since the number of IO pins of ATtiny85 is very limited, I design display and further modules should all run in I2C. Power consumption and source availability are also important factors. I choose an OLED screen drive with SSD1306. It can find in 3 different sizes, 64x32 is the smallest one. (another sizes are 128x64 and 128x32)

Other Parts

A battery holder, a small bread board, some bread board wires, two buttons and a power switch (optional).

Step 2: Assembly

Connect all the parts on the bread board:


pin 1: not connected

pin 2: set button, other button pin connect to GND

pin 3: up button, other button pin connect to GND

pin 4: GND

pin 5: OLED SDA

pin 6: not connected

pin 7: OLED SCL

pin 8: VCC

Also connect battery and OLED board to VCC and GND.

Step 3: Power Down, WDT and Time

When MCU and OLED turned on, it consume about 6 mA.

In order to make the watch can run over 1 year, I will use the MCU most power saving sleep mode, SLEEP_MODE_PWR_DOWN, when user not in use. According to my cheap power meter, it show 0.1 uA if disable all the function. But It still need to enable WDT for time keeping, after enable WDT, it show 4 uA. Assume MCU and OLED auto sleep after 5 seconds and user view the watch 12 times every day in average, the watch will consume about 0.2 mAh per day. ((0.004 mA * 24 hours) + (6 mA * (5 / 60 / 60) hours * 12)) So a 150 mAh CR2025 battery can run 750 days.

The time source code mainly come from PaulStoffregen. However, the power down sleep mode will stop the normal timer, use millis() function for time keeping is not valid. So I keep another variable to replace millis() function. For each WDT interrupt, it increase a certain value. The increment value depends on the WDT interval settings and the chip's oscillator. When using 1 second WDT interrupt, my chip's calibrated increment value is 998 (around 1000 milliseconds).

And also I have added the readVcc() function for monitoring the battery status.


WDT and power related:

time function for Arduino v1.4:


Step 4: I2C Display

The code is reference from DigisparkOLED, but since its example code complied size is over 6K, the complete example cannot put on their ATtiny85 product, Digispark or Digithumb. (it require to comment out the bitmap code for running) The complete example can only run on their another product, Digispark Pro. (it have around 14K flash available)

Here are something I have revised or rewritten:

  1. Trim out many unused data, including font and bitmap
  2. Init SSD1306 setting according to the SSD1306 data sheet page 64
  3. Try to support all known resolution (64x32, 128x32, 128x64)
  4. Support custom font
  5. Function for turning OLED on and off for power saving purpose

It use TinyWireM library, but it have a bug (reported), you need revise one line of code in write() function to cover this:

if (USI_BufIdx >= USI_BUF_SIZE - 1) return 0; // dont blow out the buffer




SSD1306 data sheet:

Another instructables using ATtiny85 and SSD1306:

Step 5: Custom Font

8K flash has no enough room to store all characters in large font size. (such as 24 pixels font height)

As watch only require 10 digital characters, we can tailor-make a selected font type binary to fit in limited space.

I will use imagicmagick command line tools to show how to convert custom font characters to a c header file.

This program need 10 digit in 2 font sizes, one with font height 8 pixels to show date digits and one with font height 24 pixels to show time digits:

convert -depth 1 -font Lucida-Sans-Unicode -pointsize 11 label:00123456789 -crop 70x8+7+4 -flip -rotate 90 watch_digit.xbm

convert -depth 1 -font Cooper-Black -pointsize 25 label:00123456789 -crop 150x24+14+4 -flip -rotate 90 watch_3x_digit.xbm

Lucida-Sans-Unicode and Cooper-Black are the font type in Windows 7, you may use your selected font type in your OS.

The corp, flip and rotate option help to adjust the binary data in correct position and direction. You may change the output format from xbm to png to preview the output bitmap.

After export the xbm files, we can copy the font binary code to the watchdigit.h source file:

#include <avr/pgmspace.h>

#define FONTWIDTH 7
#define FONT3XWIDTH 15

static const uint8_t watch_digit[] PROGMEM = {
//watch_digit.xbm binary code

static const uint8_t watch_3x_digit[] PROGMEM = {
//watch_3x_digit.xbm binary code


Step 6: Program

I am using a littlewire board as ISP, and I have make a hacking ISP connector for easier plug and play.

Any ISP that can program ATtiny85 should be ok.


Step 7: User Input

2 buttons UI for adjust time, operation method just like any simple digital watch.

Set button: select adjust field, the field will highlighted

Up button: increment value of selected field

Step 8: Calibration

Debug Screen

Press up button when not selected any field will enter debug screen.

Time Calibration

The first number is WDT interrupt count, this value is used to calibrate the value of wdt_millis_per_interrupt.

This value for you chips should be calculate from:

actual time (in millisecond) passed / WDT interrupt count

e.g. if you turn it on at 2016/01/07 23:10 and it is now 2016/01/08 13:25, it passed 51300000 milliseconds. At the same time the first line of debug screen show 51454 then you should set wdt_millis_per_interrupt as:

51300000 / 51454 ~= 997

Voltage Value

The second number is current battery voltage in millivolt. It require calibrate the constant value in readVcc() with the real readings from a multimeter.

This value for you chips should be calculate from:

actual millivolt / debug value * current voltage reference value

e.g. current voltage reference value is 1125300, debug screen second line show 2823 and multimeter show 2.81 volt then you should alter voltage reference value as:

2810 / 2823 * 1125300 ~= 1120118

Step 9: What's Next?

  • watch case, it may be a big ring on a finger (should use a smaller battery like CR1220)
  • more precise time, try to tune WDT millis with current volt and temperature
  • connect other I2C modules
  • research sync time method, GPS, WiFi + internet, BLE + mobile phone and more