Skip to content

Encoders: coarse and fine control

15 September, 2013

Hi there!

Olli aka ‘Frumpy’ posted an interesting question on the forums a little while ago:

I am doing an MCP, buttons, LEDs and now rotary encoders all work. Now comes the software part and this is certainly none of my favorites.

So the rotary encoders work. However, I want them to move at two different speeds: fast and slow spinning. Fast will do 4 steps ahead, slow only one. Think about setting a heading, you don’t want to spin the 20-click encoder 9 times to set a 180° heading change :)
This can be done with a timer I think.

{code snippet}

This should move the dataref by 1 once I touch the encoder. Next dataref change will depend on how fast I move it -> below (lets say) 30ms it will be a quick turn and move 4 steps, or slow turn then of course only one step :-)

I think this should help:
Still I look at it like a pig into a clockwork (that’s a German proverb ;-) ), I am not a programmer and certainly no C-guy. :-/

Thank you very much,


After an embarrassing amount of time where I began describing a system completely unlike what Olli described, I tested his idea and it works very well!

Here’s the code:

#include <Encoder.h>

This code links a rotary encoder to the NAV1 OBS. If you turn the
encoder quickly, the bug changes quickly and you have fast control; if
you turn the encoder slowly, the bug changes slowly and you have fine

This is an extension (and substantial rewrite) of the plain encoder-
reading code from before.

// Input from hardware
Encoder demoEncoder(7, 8); // encoder on pins 7 and 8
short demoEncoderPrevious = 0;

elapsedMillis demoEncoderClickInterval = 0;

// Input/Output with simulator
FlightSimFloat demoDataref;

void setup() {
  demoDataref = XPlaneRef("sim/cockpit2/radios/actuators/nav1_obs_deg_mag_pilot");

void loop() {

  // divide by 4 to find how many clicks the encoder's been turned
  // (+ve clockwise, -ve anticlockwise, normally)
  short demoClicks = ( - demoEncoderPrevious) / 4;

  // when encoder 'clicks' into a new detent:
  if (demoClicks != 0) {

    // change in degrees from current stored value (can be <0 )
    float demoChange = 0;

    // Threshold between FINE and FAST control: 30ms per click
    if (demoEncoderClickInterval > 30) {
      // FINE/slow mode. Change by 0.25 degrees per click.
      demoChange = demoClicks * 0.25;
    } else {
      // FAST/coarse mode. Change by 5 degrees per click.
      demoChange = demoClicks * 5.0;

    // new value = current value plus changes
    float demoNewValue = demoDataref + demoChange;

    // make sure new value is valid (i.e. when moving across 0deg/360deg)
    while (demoNewValue  <   0.0) demoNewValue += 360.0;
    while (demoNewValue >= 360.0) demoNewValue -= 360.0;

    // write validated new heading back to dataref
    demoDataref = demoNewValue;

    // reset encoder state and interval timer
    // (only when encoder has 'clicked' into a new detent!)
    demoEncoderPrevious = 0;
    demoEncoderClickInterval = 0;

It’s basically the same as the encoder code I wrote before except it has a timer (named ClickInterval) which is reset after each encoder click. The highlighted bit can be tinkered with to change the fast and slow values and the threshold between them, or replaced with a formula which gradually blends between coarse and fine control across a range of click intervals.

I’ll probably be using this for heading-bug-type controls going forwards. It’s much better than using two encoders, or one encoder and a shift button. Thanks Olli!


From → Guides

One Comment
  1. Thanks for posting this. Will be using it for sure.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s