Rotary encoder based cooking timer
- At February 20, 2010
- By Eran Duchan
- In Microcontrollers
27
Continuing my experimentations with cooking timer user interfaces, I stumbled upon a cute little rotary encoder on Sparkfun. It immediately looked like a good match for the project. The thing is that all cooking timers I’ve seen have either a simplistic interface requiring many clicks to set the time (like clicking the “minutes” button 50 times) or a complex keypad with way too many buttons.
At first I thought I’d need 2 axes (axis 1 would increment by 1 minute and the other axis by 15 minutes) but fiddling around with the trackball showed me that it was not necessary; that I’d just need one axis with tactile feedback (that is, a clicky switch rather than a smooth flowing motion of the controller) and some selection mechanism to switch between 1 and 15 minutes.
The Sparkfun rotary encoder fit the bill – 12 steps for full rotation and clickable! Perfect. I wired up the encoder on the breadboard, wrote the software and then fabricated two one layer PCBs.
The Hardware
The schematic is fairly straightforward – a PIC18F2525 sits in the middle connected to the rotary encoder (gray code and button), 3 indication LEDs and a buzzer (link to the schematic at the end of the article). The main board connects to the seven segment display mezzanine via two connectors. This board is not, in any way, efficient power wise – it sucks about as much energy as humanly possible.
4 digit Seven segment display
Another Sparkfun gem is the integrated 4 digit seven segment display. The 4 digits are internally connected and there’s even a colon in there – we save a LOT of nets and do not need to flip the 3rd digit to create a colon as there is a dedicated colon segment. The display isn’t huge, measuring in at 4 centimeters across, but will suffice for this test project. Hooking this up to the uC is very standard so I won’t dive into details. The only thing worth noting is that we need to wire up the colon anode to VCC and the cathode to the uC for controlling, holding it at logic high. When we want to light it up we sink the current on the uC by setting the cathode to logic low. You could and should ramp up/PWM this line so as to have consistent brightness with the multiplexed digits (i don’t do it in this project).
Clickable rotary encoder
The rotary encoder from Sparkfun gives two inputs: rotation (CW/CCW) and button click. The rotation interfaces takes the form of a standard gray code output: two channels connected to the uC input pins (w/external or internal pull ups) and ground. The difference between this encoder and the trackball from my previous project is that this is a purely mechanical interface, as opposed to a digital interface with the ALPS trackball. This means that while we received a perfectly filtered gray code signal from the IC on the ALPS, here we receive gray code directly from the mechanical rotation. Each rotation simply connects the channels to ground shifted by a quarter phase. In 99% of the cases we receive a good signal, but those extra 1% bounces need handling. We will see the waveforms in a bit.
The integrated button is as simple as it gets – just like any other button pulled high and shoved into the uC input.
The Software
Rotary encoder state machine
In my previous trackball project I needed to receive an event whenever the trackball moved. This was achieved very easily by simply analyzing the gray code and detecting valid gray code transitions. The difference here is that we don’t want to react whenever a bit change occurs, rather when a complete, valid cycle occurs. This cycle indicates that the user has performed one “click” on the control (there are 12 such clicks in a complete rotation). By detecting such valid cycles we filter out two unwanted events:
Normally, both channels are pulled up and sit there at logic 1. The CW flow starts by the first channel going to 0 followed by the second channel. The first channel then goes back up to 1 and so does the second. Any deviation of this flow should be filtered out, but mechanical bounces need not cause the motion to be ignored. To fulfill these requirements, I implement this using a two 3 bit “shift register” variables and a simple state machine.
The 3 bit shift registers hold the last 3 polled values of each channel. The polling rate must be fast enough such that 3 polls indicate a stable logic value (you can calculate the polling time required by measuring the shortest bit cycle you can and then diving this by at least 5 – for example if the channel bit is 1ms long when you turn the knob as fast as you can, you should set your polling to at most 200uS). The shift register value is then fed into a pattern matcher – this simply compares the value to either 111 (stable 1) or 000 (stable 0) at any given time. If a match occurs, a bit is appended to the value of the shift register – 0 for channel 0 and 1 for channel 1 (for example, if channel 1 has detected a transition to 0, the new value will be 1000, uniquely identifying an event that channel 1 has transitioned to 0). This new value is then fed into a state machine looking for a 4 consecutive values indicating the 4 step valid flow.
Let’s understand this by looking at how I detect the CW flow above. At first both channels are at 1 so the shift register value is always 111 for both channels. Since we want to detect a stable transition to 0, we set the search pattern for both channels to 000. 3 consecutive polls equal to 0 satisfy the requirements of debouncing mechanical spikes. Once channel 0 goes to 0 for more than 3 polling cycles, the pattern match occurs and the value is fed to the appending mechanism which appends 0 (for channel 0). This value is then fed into the state machine which sees that it indicates the first step of the 4 step flow (channel 0 went down to zero).
The pattern matching for channel 0 is then set to 111 (we want to know when the channel goes back to 1) and the state machine is set to expect the next step: 1000 (channel 1 goes down to 0). In a valid flow channel 1 goes down to zero, the pattern match occurs, 1 is appended to the shift register and the value is fed to the state machine. The search pattern for channel 1 is then changed to 111 and the state machine is set to expect the next step: channel 1 goes back up to 1.
This flow continues until the entire flow occurs correctly. If at any point something is done in the wrong order, the entire state machine and pattern matching resets. This mechanism filters out any “half cycles” performed by the user. Only when the user clicks the rotary to the next notch will the above flow occur. This state machine is written in quadenc.c: quadenc_isr().
Seven segment display
Operating the 4 digit SSD is exactly the same as operating 4 separate seven segments – you select a digit via a transistor, illuminate the desired segments, pause, select next digit and repeat. There are tons of tutorials on seven segments out there so I won’t dive into details. The only thing worth mentioning here is that in my implementation I do two obvious things:
- Whenever I want to display a number I modify a RAM array containing 4 bytes – each byte representing a digit and each bit representing the appropriate segment (the position into the byte is the position of the segment in the I/O port). Doing this does not affect the value displayed to the user – it’s all done in RAM – see sseg.c: sseg_setDisplay().
- A timer interrupt (shared with the rotary encoder polling mechanism) is set to occur ever X interval (a few milliseconds is fine). When the interrupt is raised I load the next byte (of the 4 bytes modified by the application) into the port. Can’t get any shorter than that. See sseg.c: sseg_isr().
The user interface is a standard state machine driven user interface. The video below has a short walkthrough of the interface. As such, I’ll focus on the button event handling mechanism because this is somewhat reusable over projects.
Just like for every mechanical button, software debouncing is required. This is done by polling the input pin every X ms and shifting this into a 3 bit shift register (no interrupt is used). Once a stable pattern is detected (either 000 for button down, 111 for button up) we can handle this event. However, our timer exports a few cases for the user:
Video demonstration and waveform explanation
The following video gives an overview of the timer user interface (interesting unto itself) and then at just over five minutes in shows waveforms for working with the rotary encoder.
Related Downloads
- Schematics in Eagle and images (main board schematic, display mezzanine board schematic)
- Corrected Sparkfun 4 digit seven segment display eagle part (not part of the Sparkfun library, but imported from a Sparkfun project and fixed package)
- Full source
- Hex file


LK
Thanks, that was just the information i needed about the rotary encoders. Video was very helpful.
Aaron
Great info! I am starting a timer project as well, but in the milli-second range to delay en event after a trigger. This info is great for setting timing down to single milli-second range. Thanks for the great work.
Alex
I need hex, please?
Saimneo
Dear Eran Duchan, i really inspired with ur this rotary cooking timer. I really new to electronics and i really want to built this cooking timer for my home made project gas oven. Could you please help me to build this project? plz if possible email me its PCB design,diagram,parts, programming. it will be great help.My email address saimneo@yahoo.com. Thanks
Eran Duchan
Hey Saimneo. The code and eagle PCB are available on the page – just look at “related downloads” above.
Ishan Karve
Nice piece of logic Eran. Really appreciate your menu logic. a Great video 2..
Keep up the nice work.
SL
Hi, I was just wondering whether there is any way of opening your code in Arduino?
Thanks
Eran Duchan
Well, since the code is written in C you can re-use 95% of it on pretty much any platform. The other 5% would have to be re-implemented specifically for your processor architecture (things like setting/getting/configuring GPIO pins, starting a timer, handling the timer interrupt, and any other interaction with hardware).
Tom M
Hey Eran, I loved this design, so simple and elegant. I would really like to hear more about state machines. I have been playing around on the Arduino platform and I like most beginners code linearly. I recently was reading about the QP framework which offers an Arduino implementation. I understand if I ever want to make responsive projects, I am going to have to learn this, but the content so far is way above my head. The resources I have looked at so far seem to have a very steep learning curve. Can you recommend any resources for someone new to state machines? I would also love to hear more about how you implemented state machines in to this project. This type of project sounds like a great example for someone new to state machines. Thanks, -Tom
Eran Duchan
Hi Tom
I don’t know of any specific resources regarding state machines (I’m sure there are many tutorials on google), but the concept is so simple to grasp and implement that I think you may be confused regarding the scope of state machines. In this project, the state machines are implemented using a single variable (which state I am in) and a simple switch statement which receives an event, checks which state the system is in and reacts accordingly.
The concept of detecting and dispatching events is the more open-ended problem here. How do you get a simple microcontroller to poll and handle so many events at a given time, but still be able to be responsive? This is where the meat is and there are TONS of things to learn. From a simple while dispatch loop to full blown real time operating systems. It is my belief that with an 8 bit microcontroller you cannot really implement an elegant solution as you are bounded by performance and memory constraints. I would try to work with higher end ARM based microcontrollers and FreeRTOS; this will give you a MUCH more suitable platform to learn these concepts.
Good luck
Eran
Jose SCOURNEAU
Hello,
congratulation for your project !
It’s possible for you to send me the HEX file and fuse configuration if not included in the hex file for programming the chip ?
Thank you,
Jose
Eran Duchan
I’ve added the hex (read directly from the PIC) to the post. Enjoy.
Jose SCOURNEAU
Hello,
thank you for the code, it work perfectly !
Would it be possible to add an output to control a relay during the countdown in Pin 6 (RA4) ?
It’s for a replacement of the mechanical timer on my dryer
Thank you,
Jose
KISS
I was thinking about this today and decided to search for stuff to see what I could find. Not bad.
You say push and hold selects a timer. What does that mean? Is it multiple timers or does it select hours/seconds?
What I would like is the ability to control an output such as a opto isolated open collector output.
You never mentioned if the digits being set blink or anything.
Another requirement for me would be the ability to have short or infinate buzzer sounding.
I could see putting in something in the oven downstairs, setting the timer and have the output sound the buzzer forever until silenced. The isolated output could blink the lights or cause a text message to be sent to your phone via an home automation system.
Total elapsed time would also be useful. i.e. Your cooking something and have to turn it. You silence the timer, but it is still keeping track of total elapsed time. Finally, I’d add the ability to be able to see what the clock tie would be when the timer wend off.
The timer should continue to count in elapsed time units until the timer is reset.
You could do a lot of neat things with this.
Eran Duchan
Lots of things to address…
> push and hold selects a timer
See the vid. There are three timers that can work simultaneously. You switch between them by pushing and holding the knob. This way you can have 3 things cooking and manage a timer for each
> the digits being set blink or anything
They digits don’t blink because the time is set in 1m or 15m increments
> infinite buzzer, blink the lights or cause a text message, Total elapsed time
Anything is possible. The schematics are there, the source is there… feel free :) This was just a weekend project for me. I have no plans for it
Thanks for the feedback!
Jenda
Hi Eran,
very nice solution indeed and the way user interacts is very intuitive. I may have one enhavement for this. Once you set the timer the respective LED should start to blink, so when you set two timers, you know by sight which ones and when the timer ends and is not selected, the blink speed might be fast so user know that the rice is done while chicken still needs some time..
Thanks again for this tutorial.
LD
Great video! That was really helpful to understand that encoder
Валерий
Hello.
It would be desirable to repeat this creation of hands human but there is no drawing a payment.
Eran Duchan
If you mean that you’d like to recreate the project – you can download the schematics and even the PIC .hex file from the bottom of the post. People have duplicated this project a few times already (as homework, I would assume).
Валерий
Me drawing of payments of the indicator and the mainframe interests.
Preparing a tiny seven segment display PCB
dada
Hello! it’s very interesting. i not have this PIC18F2525 and i have PIC18F2550. Can work PIC18F2550 of timer? can you help me pls
Eran Duchan
Go to Microchip’s PIC18 page. You can compare different MCUs and derive if one can replace the other in a given project. A quick look shows that the two processors share basically the same peripherals (though the 2550 seems to have less flash/sram/eeprom memory).
dada
Hi again me. Do you have 2550 of digital timer project? Or PIC16F84. I search digital timer project in internet. An reality work digital timer project i not found. I believe you reliability digital timer project. Help me
Eran Duchan
Sorry, I don’t have any such thing. As I said – they should be fairly compatible. If not, you’ll have to dive into the datasheets and do the modifications.
Eric PETIT
Great project Eran !!!!
I am very interested by the way in which you treated the encoder.
I tried to download the source file but I am not able to read it.
What is the extension of this file?
Thank you for your help.
Best regards
Eran Duchan
Thanks Eric :)
The extension is 7zip – it simply compresses much better than zip and such.
Eric PETIT
Thank you Eran.
I didn’t know this format.
I am on Macintosh but with a Windows Emulator all is OK :-)
I will be able to study the original and effective method with which you treated the encoder.
Thanks again.