Rotary encoder based cooking timer

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:

  • Mechanical bounces (see rightmost waveform below)
  • The user overshooting and performing an extra “half” click (see middle waveform below)
  • The software needs to look for two certain valid 4 step flows: a CW click and a CCW click, as shown in the leftmost waveform.


    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:

    1. 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().
    2. 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:

  • Single click: pausing an active timer or switching between 1 and 15 minute intervals
  • Double click: Reseting the timer
  • Long press: Start a timer
  • Press and turn: Select a timer
  • This requires some code to handle, done in a state machine separate from the user state machine (see ui.c: ui_checkButtonEvent()). The outputted events from this button handling machine are fed into user interface to actually perform the desired event.

    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


    27 comments


    • LK

      Thanks, that was just the information i needed about the rotary encoders. Video was very helpful.

      May 3, 2010
    • 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.

      June 10, 2010
    • Alex

      I need hex, please?

      February 16, 2011
    • 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

      March 10, 2011
      • Eran Duchan

        Hey Saimneo. The code and eagle PCB are available on the page – just look at “related downloads” above.

        March 10, 2011
    • Ishan Karve

      Nice piece of logic Eran. Really appreciate your menu logic. a Great video 2..
      Keep up the nice work.

      April 11, 2011
    • SL

      Hi, I was just wondering whether there is any way of opening your code in Arduino?
      Thanks

      May 11, 2011
      • 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).

        May 12, 2011
    • 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

      July 1, 2011
      • 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

        July 1, 2011
    • 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

      July 31, 2011
      • Eran Duchan

        I’ve added the hex (read directly from the PIC) to the post. Enjoy.

        August 1, 2011
      • 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

        August 21, 2011
    • 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.

      August 4, 2011
      • 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!

        August 4, 2011
    • 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.

      August 16, 2011
    • LD

      Great video! That was really helpful to understand that encoder

      August 28, 2011
    • Валерий

      Hello.
      It would be desirable to repeat this creation of hands human but there is no drawing a payment.

      September 22, 2011
      • 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).

        September 22, 2011
    • Валерий

      Me drawing of payments of the indicator and the mainframe interests.
      Preparing a tiny seven segment display PCB

      September 23, 2011
    • 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

      December 12, 2011
      • 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).

        December 13, 2011
    • 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

      December 14, 2011
      • 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.

        December 14, 2011
    • 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

      December 28, 2011
      • Eran Duchan

        Thanks Eric :)
        The extension is 7zip – it simply compresses much better than zip and such.

        December 28, 2011
      • 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.

        December 28, 2011

    Leave a comment


    Name*

    Email(will not be published)*

    Website

    Your comment*

    Submit Comment

    © Copyright pavius.net: no refunds - Designed by Pexeto