The final timer.

An upgrade to the original grinder_timer - a programmable timer tethered to a Rancilio Rocky coffee grinder.

Honestly, I use the original every day and it hass been working flawlessly for a few years, but the one thing that always bugged me about it was the mushy keypad buttons. I was looking for a new project to start, so I decided to re-design and build from scratch a version with some nicer tactile buttons and a few other improvements.

The goals for revision 2 included:

Apart from the changes listed above, the features for rev2 will be otherwise identical to the original:

Code, schematic and PCB layout are all in the gitlab repo. This is the same repo used for the original version.


The final timer in action.

The user has 4 available timer presets - A, B, C and D. A preset is selected by scrolling left or right with the d-pad. To configure a preset, scrolling up or down increases or decreases the value of the timer in increments of 0.25 seconds. The maximum configurable timer is 60 seconds and the minimum is one second. A preset value is saved to eeprom whenever it is used or a different preset is selected.

Once a desired timer is set, pressing the grind button will turn on the grind motor and begin counting down to zero. Once zero is reached, the grinder is turned off and the timer resets to the value of the preset. Pressing any button whilst the grinder is running cancels the grind (turns the motor off) and resets the timer.

After 120 seconds of inactivity, the timer enters a "sleep mode" whereby the OLED is deactivated. Pressing any button will take the timer out of sleep mode.

The grind button is illuminated when it is ready to use and off while a grind is underway. The LED pulses while the timer is in sleep mode.


The schematic and PCB were designed using KiCad. The previous PCB version I fabricated myself using single-sided copper clad-board and copper etchant (i.e. the toner-transfer method). This time I uploaded the gerber files and had the board fabricated by JLCPCB.

I found some surface-mount tactile buttons with keycaps for the keypad. The four direction buttons are identical, the grind button is similar but with a built-in white LED. This LED was connected to a pin with PWM output capability to enable variable brightness.

A suitable aluminium enclosure was selected and its internal dimensions dictated the dimensions for the PCB design.


In developing a recent project (temp0) I tried out an Arduino platform and programming. I didn't care much for the Arduino IDE, but I learned how C++ brings some advantages to firmware programming. So for grinder_timer_rev2 I re-wrote the firmware from scratch and made use of classes. I still opted to directly program the microcontroller as I saw no advantages in using the Arduino bootloader.

This is my second project with which I have used the PlatformIO IDE. It's still great, though it took a bit of work figuring out how to directly program an ATmega328PB with a TinyISP programmer (i.e. with no Arduino or other dev board).

Classes everywhere! I defined a bunch of classes in an attempt to keep the overall firmware modular.

Class Name Description
clock rtc Uses a timer/counter and the 32.768kHz crystal oscillator to create a real-time clock for acurate timing. Configures to trigger an interrupt every 16th of a second when running.
i2c twi I2C / I2C / IIC / TWI interface. Used within the sh1106 class for comms from the microcontroller to the OLED controller.
keypad buttons 5-button keypad. 4-button d-pad (up, down, left, right) and a "grind" button.
presets preset Variables and eeprom addresses for saving, restoring and referncing the values of four presets, plus the currently selected preset. Being saved in eeprom means these values are retained through a power-cycle.
pulser led Uses a timer/counter pulsing, and another timer/counter for pwm to set an LED on, off or pulsing.
relay grinder Just an output pin connected to a relay. In this case, the relay will control the grinder motor.
sh1106 oled Drive a 128x64 pixel oled with a sh1106 controller. Includes functions for (among other things), printing text, displaying bitmaps, vertical scrolling and drawing boxes.
sleeper sleep_timer Uses a timer/counter to trigger an interrupt after a set duration to enter a "sleep" mode.
usart serial Serial interface - really only used for debugging, disabled in the final code.

The biggest problem I encountered was with stability of the OLED. It would appear to operate as expected, but after random intervals it would hang. I located the stall point in the I2C function that waits for completion of an I2C transmission. After experimenting with different clock speeds and power sources, eventually I figured out that the values of the pull-up resistors I had on the SDA and SCL lines were too high. After replacing these 10k resistors with 3.3k resistors, the OLED became stable and predictable.