5898

A small portable device featuring timer, clock, and calendar functions, equipped with a simple and functional interface and a truly long battery life.

From Idea To Realization

A few years ago, I built a kitchen timer that turned out to be the most appreciated and frequently used project in my family. It utilized a four-digit seven-segment LED display and an ATmega328 microcontroller. However, it had one major flaw: it required frequent recharging of its small battery.
I therefore conceived an updated version with the goal of achieving the lowest possible current consumption, adding clock and calendar functions, and reducing the user interface to the bare minimum. To achieve these results, lacking access to specialized industrial chips, I had to pay particular attention to component selection from those easily available on the market. I also focused on optimizing both the circuit and the firmware, implementing tricks derived from both practical experimentation and a deep dive into low-power management techniques.

The Circuit

As can be seen in the schematic in Figure 1, the circuit is built around the microcontroller IC1, a Microchip ATtiny3216 [1] which manages the display, the user interface, and the data arriving from IC2, a DS3231M RTC (Real Time Clock) integrated circuit.
 
Power for the entire circuit is supplied by a 3.7V, 720mAh rechargeable Lithium Polymer battery, via IC3, a 3.3V LDO (low-drop regulator) voltage regulator. A small module with a USB-C interface and protection circuit provides for battery charging.
 
When the device is not in use, the microcontroller is in SLEEP_MODE_PWR_DOWN mode, and the current drawn by the entire circuit in this state is about  5 µA, certainly lower than the battery's self-discharge current. Therefore, no power switch is provided.

01-20260128182943.png
                                                         
                                                            Figure 1: The schematic diagram

The operating logic allows the MCU to be "woken up" by three distinct events to perform the desired functions:

  • Pressing the "Set" button to set the time and date, via the rotary encoder, the first time the circuit is powered.

  • Closing of the "Tilt Switch" in case of rotation of the board by 90° counterclockwise with respect to the default horizontal position, for display of time and date followed by automatic return to sleep mode.

  • Pressing the rotary encoder "Switch" to activate the timer function. The desired time and the timer status will be controlled by the encoder itself.
When any of these events occur, the execution of the other two is automatically blocked until the current function finishes and the device returns to sleep mode. In each of the three active modes (which involve turning on the display), the current consumption of the entire circuit is typically 2 mA.
 
The ATtiny3216 clock frequency is set to 1 MHz to minimize consumption while maintaining sufficient execution speed for the  I²C and SPI interfaces. This MCU offers the necessary number of I/O pins and the convenient, fast UPDI (Unified Program and Debug Interface), which requires only a single data line for programming. C1 and C2 are the standard bypass capacitors.
 
The display (LCD1) is a single-row, eight-character text LCD. It is visible without a backlight, consumes low power, operates directly at 3.3V, and interfaces with the microcontroller via 4-wire SPI. It requires two external capacitors, C3 and C4. It can be equipped with a LED backlight unit (available in various colors) for better visibility in low-light conditions. I chose red for LED1 due to its lower current demand.
The full documentation is available online from the manufacturer [2] [3], DISPLAY VISIONS. Figure 2 shows the configuration used in this project.
02.jpg
                        Figure 2: Display in 3.3V - 4 Wire SPI configuration, from the datasheet

Note that the backlight unit is connected to a separate terminal of the microcontroller, via the protection resistor R5. The LEDs are in fact driven by a PWM signal at 490 Hz (frequency resulting from the 1 MHz clock), allowing fine adjustment of the brightness via software, simply by setting the duty cycle value (BL_VALUE) in the analogWrite(DISPLAY_BL_PIN, BL_VALUE); call between 0 (off) and 255 (max). In Figure 3, you can see the PWM signal with a duty cycle of 7.8% (BL_VALUE = 20) captured with the oscilloscope on pin PB2.

03-20260128184948.png

                                   Figure 3: PWM 490Hz backlight power supply - Duty Cycle 7.8%

Connected to the microcontroller's I²C bus is IC2, a DS3231M [4] real-time clock chip with an integrated MEMS resonator, responsible for maintaining time and date. The accuracy stated by the manufacturer (Analog Devices / Maxim Integrated) is ±0.432 Seconds/Day. The schematic configuration might appear incorrect (VBAT pin connected to 3.3V and VCC pin to GND), but this is documented in the datasheet as "Single Supply (VBAT Only)" mode, reducing consumption to just 2 µA when the I²C bus is inactive. R2 and R3 are the necessary pull-up resistors, and C7 is the recommended bypass capacitor.
 
The LDO regulator IC3 (Microchip MCP1702-3302E) was chosen for its extremely low quiescent current, typically 2 µA. Furthermore, with a load of a few mA, the voltage drop is in the order of tens of mV, allowing for optimal usage of battery capacity. C8 and C9 are the bypass capacitors recommended for regulator stability. 
 
The rechargeable battery is a 3.7V LiPo (Lithium Polymer) element with a capacity of 720mAh, featuring a built-in protection circuit against excessive charge and discharge. It is managed by a small charging module, easily available commercially, based on the TP4057 chip, which includes its own protection circuit, two indicator LEDs (red - charging, blue - charge complete), and the modern standard USB-C interface for the charger. The battery and charger module can optionally be eliminated and replaced by three 1.5V AAA alkaline batteries in series. Even with this option, one can expect over a year of battery life, assuming one hour of daily device usage. 
 
I mentioned earlier that the time and date display is activated by a tilt sensor (Tilt Switch). This is a very common type of electromechanical switch, SW4 in the schematic (model SW-520D), mounted on the board so that it closes the contact when tilted approximately 90° relative to the resting position.
 
However, the connection to pin PB4 of the microcontroller might seem unusual: not direct, but via C5 and R4 (while C6 serves only to limit noise due to contact bounce). PB4 is normally held HIGH (VDD) by the internal pull-up resistor configured via software, with a value of about 50 kΩ for the ATtiny3216.
 
We know that SW4 needs to wake the MCU to display the date and time, and the way to do this is to pull PB4 LOW (GND), triggering an interrupt which in turn executes an ISR (Interrupt Service Routine) that finally runs the necessary code. The subject of interrupts is rather complex, and this series of MCUs offers numerous configuration options. For further details, I refer to the specific documentation provided by the author of the megaTinyCore [5], the software that allows programming AVR ATtiny Series 0/1/2 microcontrollers from the Arduino IDE.
 
In practice, I realized that to ensure correct execution of the interrupt, the display cycle, and the return to sleep mode, it was necessary to have a single short level change (from HIGH to LOW and back to HIGH) on pin PB4, even if the sensor contact remained closed (i.e., the device was left tilted).
 
Capacitor C5 provides exactly this function. PB4 is normally at a HIGH level (3.3 V). When SW4 closes to GND, the capacitor charges through the pull-up resistor, causing a voltage drop (LOW level) on PB4. Once charged, the level on the pin returns HIGH and remains HIGH even if SW4 stays closed. When SW4 opens, C5 discharges via the parallel resistor R4, and a new cycle can begin.
 
The value of R4 must be such that with SW4 closed, the voltage divider formed between the pull-up resistor and R4 itself allows PB4 to remain at a voltage corresponding to a logic HIGH level. In this case, we get about 2.5 V, which is above the minimum datasheet specification (0.7 x VDD, i.e., 0.7 x 3.3 = 2.31V).
Since a picture is often worth a thousand words, Figure 4 shows the graphical representation (captured with an oscilloscope) of the voltage across SW4.

04.png
                                             
                                                 Figure 4: Voltage on PB4 upon actuation of SW4

One can clearly observe the voltage dropping from 3.3 V to almost zero upon the closing of SW4, rising exponentially until C5 is fully charged, remaining at the value determined by the pull-up/R4 divider (2.54 V, HIGH level), and finally returning to 3.3 V upon opening the contact. This trick is useful when a "one-shot" pulse is needed regardless of the contact reopening time.
 
Completing the circuit is the piezoelectric buzzer with a series resistor limiting its sound emission level, and switch SW1 to completely silence the device if required.

Prototype Construction

The prototype was built, as usual for me, on perf-board. This time I used two rectangular stackable boards separated by spacers, as shown in Figure 5.

05.png

                                                        Figure 5: The two prototype boards

The upper one, visible in Figure 6, hosts the circuit related to the display and the backlight unit and is connected to the underlying board with a short length of eight-pole rainbow ribbon cable. The display features twenty in-line contacts on one side and another four on the opposite side, reserved for the two LEDs of the backlight unit. It was soldered directly onto the board to avoid instability and poor contacts.
06.png

                                                         Figure 6: Display board, solder side

The lower main board shown in Figure 7 and Figure 8 includes all the remaining circuitry, except the battery, which can be placed wherever desired and connected with the appropriate JST-HX connector.

07.png
                                                             Figure 7: Main board, component side

The ATtiny3216 microcontroller and the DS3231M integrated circuit are available only in SMD format, so  they were soldered onto adapters and inserted into low-profile sockets.
The charging module, however, is soldered directly to the board. The rotary encoder used is also an SMD type but was adapted for through-hole mounting. It is model EVQWKL001 manufactured by Panasonic and seems to be available only on the Asian market. It is very compact and robust, despite appearances. If unavailable or disliked, it can be replaced with any type of rotary encoder with a switch.
 
Also note, in Figure 7, the mounting of the SW-520D tilt switch and the alignment of its leads. It must be mounted exactly like this to achieve switching at a board inclination angle of no less than 80-90°.
 
The programming connector, in the top right corner, exposes the connection to GND and the UPDI data line, which leads to pin PA0 of the microcontroller. No power line is provided as the device is always connected to the battery. For an in-depth discussion on building a simple UPDI programmer [6] (a USB-to-serial adapter and a diode are sufficient), I refer to the specific documentation provided by the megaTinyCore author.
08-20260128225243.png
                                                 
                                                           Figure 8: Main board, solder side

On the solder side, in Figure 8, note that the passive components are surface-mount types. With a little patience (and perhaps a good magnifying glass), SMD resistors and capacitors of the 1206 and 0805 series can be soldered onto a perf-board without excessive difficulty. This results in significant space savings and shorter connections between components.
 
Figure 9 shows a detail of the battery used. It is strongly recommended that it has a built-in protection circuit (the small PCB encapsulated by yellow film) and comes from a reliable supplier.
09.png
                                                   Figure 9: The battery used in the project

Save The Power

The spirit of the project is clearly the quest for the lowest possible current consumption. Let's look in more detail at how this goal was pursued and if values extrapolated from component datasheets found practical confirmation in reality. Where data is available, the reference temperature is 25°C and the supply voltage is 3.3 V. For fairness, I must state that I performed current measurements with a common digital multimeter (UNI-T), so some tolerance in absolute values should be expected, though the orders of magnitude are correct.
 
  • For the ATtiny3216 microcontroller, Microchip graphs indicate a consumption of 350 µA in active mode at a 1 MHz clock frequency. It seems this figure does not account for instructions being executed and/or connected peripherals (e.g., I²C and SPI buses, pull-up resistors).
 
  • The DS3231M RTC chip, in the configuration used in the project, should draw a maximum of 150 µA with the I²C bus active.

  • The display manufacturer indicates, for the EA DOG series, a typical consumption of just 250 µA in full operation.

  •  The chosen backlight module contains two red LEDs connected in parallel with each other and via a 100 Ω limiting resistor to MCU pin PB2. The forward voltage drop of the LEDs is 2.1 V, so the current should be I = (3.3 – 2.1) / 100 = 12 mA. As mentioned, the LEDs are driven by a PWM signal with a duty cycle of 7.8%, as seen in Figure 3. The effective average current drawn by the LEDs therefore becomes I = 12 x 0.078 ≃ 0.94 mA, still sufficient to ensure excellent visibility in low light.
  
Summing the obtained values yields a total of 1.69 mA, which, considering tolerances and accepted simplifications, is consistent with the expected current consumption
 
A similar verification can be done for the Power-Down (sleep) mode of the device.
The values reported below are the typical ones indicated in the datasheets, but they are very close to those measured on the prototype.
 
Let's consider in this case the display and the backlight unit not connected to power (pins PB2 and PB3 at LOW level).
 
  • For the microcontroller, consumption is 0.1 µA, considering all peripherals disabled (instruction set_sleep_mode(SLEEP_MODE_PWR_DOWN);).

  • The DS3231M RTC draws 2 µA with the I²C bus inactive.

  • The LDO voltage regulator consumes at least 2 µA even with no load.

  • The charging module with TP4057 constantly draws about 1 µA from the battery.
 
As can be seen, the sum of the partial values is 5.1 µA, very close to the value detected on the finished prototype. It is a pity that in the first measurements, on the circuit still assembled on the breadboard, the current absorbed by the microcontroller turned out to be at least two orders of magnitude higher and with non-constant values (indeed, forums are full of users complaining about not being able to obtain the expected minimum current in similar cases).
 
Following countless tests and measurements, it became evident that any peripheral connected to the microcontroller can unpredictably drain current from the pins depending on their configuration (INPUT, INPUT_PULLUP, OUTPUT) and state (LOW, HIGH) at the moment Power-Down mode is enabled. With reference to the schematic in Figure 1, the culprits of the anomaly proved to be the display and the rotary encoder. For the former, the cause is the type of internal circuitry, which is effectively impossible to predict or modify. For the latter, the explanation, after some reflection, was evident but perhaps less obvious. 
 
A rotary encoder essentially consists of two contacts that open and close alternately as a result of the rotation of a shaft, generating two so-called quadrature signals, i.e., phase-shifted by 90°. The two contacts have a common terminal (C in the schematic), connected to GND, while the other two (A and B) lead to pins PA7 and PA5 of the MCU, which are configured via software as INPUT_PULLUP inputs with an internal resistor value of about 50 kΩ connected to VDD (3.3V). In this way, the inputs are at a HIGH level when the encoder contacts are open and at a LOW level when they are closed. 
 
It is almost certain that at the moment of activating the Power-Down state, one of the contacts will be closed, causing a current to flow through the pull-up resistor equal to
I = 3.3 V / 50 kΩ = 66 µA, very low certainly, but still 660 times higher than the expected 0.1 µA. Besides this, just connecting the SPI bus and the display reset terminal caused the total sleep consumption to reach the unacceptable value of almost 1 mA. 
 
I can anticipate that all unwanted current consumption was eliminated by intervening with an effective "software cure," as we will see better later, but this demonstrates how it is often neither immediate nor guaranteed to obtain practical results consistent with theoretical expectations.

The Software

I wrote the program that animates the project with Arduino IDE 1.8.19 after installing megaTinyCore version 2.6.10 by SpenceKonde [7], available from the Boards Manager.
Installation of three libraries is required (DSM3231, ESPRotary, OneButton) via the Library Manager, and manual installation for the DOGM_7036 display library. Library files are available for download, along with the sketch file ATtiny3216_Timer_Clock.ino, which contains Arduino IDE configuration instructions, code comments, and further useful information and links. As mentioned, firmware loading uses the UPDI interface via a simple-to-build programmer. I have also made the relative .HEX file available for completeness (ATtiny3216_Timer_Clock.hex).
 
The code follows the classic Arduino sketch structure. It begins with including necessary libraries, pin and constant definitions, creating instances of used objects (display, encoder, buttons, RTC), and declaring global variables.
 
This is followed by the setup() routine, where pins are configured (including unused ones to avoid floating inputs), sleep mode is set (SLEEP_MODE_PWR_DOWN), and the RTC and display are initialized. Initialization of encoder and button instances is then completed, linking them to their respective operational functions.
 
The next function, loop(), is the main routine of the sketch and runs continuously when the microcontroller is active, handling all operations. The main ones are:
 
  • Monitoring the state of the buttons and encoder if the count is not active.

  • Timer logic: if the countRun flag is active, it calculates and displays the remaining seconds. If the target reaches zero, it triggers the "blink" (alarm) state.
 
  • Alarm management: manages the blinking of the display ("End") and the buzzer via the blinkCounter counter variable, in non-blocking mode.
 
  • Timeout and sleep: if there is no activity (timer stopped or not set) for 10 seconds, the goSleep() function is called.
 
The goSleep() function deserves particular attention as it must allow the device to enter inactive mode with a total current absorption of 5 µA and to wake up correctly in the required operating mode.
 
Let's then see the fundamental operations performed within this routine, including the measures necessary to avoid the unwanted current requests highlighted previously:
code.png

Finally, inside goSleep(), conditional expressions are executed that determine, based on the state of specific global flags (previously reset), which of the following three operating modes will be implemented upon MCU wake-up, blocking the other two (the relative flags remain false).
 
  • Timer mode: activated by pressing the encoder button. The user then rotates the knob to set the time (function rotate(ESPRotary& encoder) ). One click starts the countdown, another pauses it.
 
  • Clock mode: activated by the tilt sensor. Briefly shows current date and time retrieved from the RTC (function displayTimeData(); ), then goes back to sleep.
 
  • Set mode: activated by pressing the "Set" button. Allows configuration of Year, Month, Day, Hour, and Minutes by navigating a sequential menu via encoder and switch-case instructions (function rotate(ESPRotary& encoder) ).
 
Two interrupt vectors identify the wake-up source by executing the associated Interrupt Service Routine (ISR), a special function that modifies the global flags. Specifically:
 
  • ISR(PORTA_PORT_vect) handles the "Set" button (Pin 15) and the encoder switch (Pin 2), setting the setMode or timerMode flags.
 
  • ISR(PORTB_PORT_vect) handles the tilt sensor (Pin 5) to activate the clockMode flag.
 
Other relevant functions external to loop(), which could be defined as auxiliary, are:
 
  • displayTimeData(); formats date and time from the RTC, handling information display.
 
  • checkBatteryVoltage(); monitors supply voltage using internal reading with the readSupplyVoltage() function, a specific extension of the AVR library. If VCC drops below 3.1 V, it signals "Low Batt" on the display.
 
  • playPwmBeep(); handles acoustic feedback via PWM on the buzzer, respecting the global beepOn setting.
 
The complete code consists of several hundred lines, and I advise programming enthusiasts or those interested in making modifications to examine it by opening it in the Arduino editor. I tried to use self-explanatory names for functions and variables as much as possible.

Component List

Resistors
R1, R2, R3 = 4,7kΩ (all 1/4W 1%)
R4 = 100kΩ
R5 = 100Ω

Capacitors
C1, C5 = 100nF 50V ceramic multilayer X7R
C2 = 100µF 10V electrolytic
C3, C4, C7, C8, C9 = 1µF 50V ceramic multilayer X7R
C6 = 1nF 50V ceramic multilayer X7R

Semiconductors
IC1 = ATtiny3216
IC2 = DS3231M
IC3 =  MCP1702-3302E

Miscellaneous
LCD1 = Display EA DOGM081W-A DISPLAY VISIONS
LED1 = Backlight module EA LED55X31-R DISPLAY VISIONS
U1 = Charger module TP4057 with battery protection
SW1 = Rotary Encoder with Switch EVQWKL001 Panasonic
SW2 = Push Button (normally open)
SW3 = Tilt Switch SW-520D
BUZZER = Piezo Buzzer
BAT1 = LiPo Battery 3.7V 720mAh whit protection

Final Considerations

I hope that the main theme of this article (minimising current consumption in a microcontroller device), together with the intention of creating a useful device equipped with a practical and original user interface, has made the project interesting both technically and educationally, and can provide some useful inspiration for your low-power projects, in the pure spirit of passion for electronics and programming. Finally, it is possible to view a short demonstration video on YouTube [8].

References And Useful WEB Links

[1] Microchip ATtiny3216 datasheet