max with a rotary encoder

Did you manage to get this working?

I can't get crimony's code to work either - returns a lot of errors when checked.

Anyway, I think you need the common to ground:

  --------------
  |                |
  |  encoder   |
  |                |
  --------------
   |       |      |  
Pin 2   GND  Pin 4

And turn on pull-ups:

void setup()
{
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH); 
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH); 

  attachInterrupt(0, DebounceSwitch, CHANGE);

  Serial.begin(9600);
}

void loop()
{  
  Serial.println(encoder0Pos);
}

i got it working, but did write my "own" little library based on some others... and yes, it has to be connected to ground. Heres the library code:

cpp-header-file:

/******************************************************************************
 *  RotaryEncoder.h - Arduino library for reading RotaryEncoder encoders.
 *  Version 0.90
 ******************************************************************************/

#ifndef RotaryEncoder_h
#define RotaryEncoder_h

#define DIGITAL_PINS (13)

class RotaryEncoder
{
    public:
            RotaryEncoder(int encoderPin1, int encoderPin2, int buttonPin);

            void minimum(int min);
            int minimum(void);
            void maximum(int max);
            int maximum(void);
            void nominimum(void);
            void nomaximum(void);

            int position(void);
            void position(int pos);

            int pressed(void);

            static void isr(void);

    private:
            int _readEncoderPins(void);
            int _readButtonPin(void);
            int _encoderPin1, _encoderPin2, _buttonPin;

            volatile int _position;

            int _min, _max;
            bool _usingmin, _usingmax, _buttonState, _buttonPrevious;

            volatile int _previous, _time;

            static RotaryEncoder* _instance;
};

inline void RotaryEncoder::minimum(int min)
{
    _min = min;
    _usingmin = 1;

    //  adjust position if lower than new minimum
    //_position = max(_position, min);
      _position = _position > min ? _position : min;
}

inline int RotaryEncoder::minimum()
{
    return _min;
}

inline void RotaryEncoder::maximum(int max)
{
    _max = max;
    _usingmax = 1;

    //  adjust position if higher than new maximum
    //_position = min(_position, max);
      _position = _position < max ? _position : max;
}

inline int RotaryEncoder::maximum()
{
    return _max;
}

inline void RotaryEncoder::nominimum(void)
{
    _usingmin = 0;
}

inline void RotaryEncoder::nomaximum(void)
{
    _usingmax = 0;
}


inline int RotaryEncoder::position(void)
{
    return _position;
}

inline void RotaryEncoder::position(int pos)
{
    _position = pos;
}

inline int RotaryEncoder::pressed(void)
{
    return _buttonState;
}
#endif // RotaryEncoder_h

cpp-file:

/******************************************************************************
 *  RotaryEncoder.cpp - Arduino library for reading RotaryEncoder encoders.
 *  Version 0.90
 ******************************************************************************/

#include "RotaryEncoder.h"

extern "C" {
      #include <inttypes.h>
      #include <avr/interrupt.h>
      #include "WConstants.h"      //all things wiring / arduino
}

const int _quadrature [4][4] = {
    { 0, 0, 0, 0 },            //  00 -> 10 is silent CW
    { 1, 0, 0, 0 },      //  01 -> 00 is CW
    { -1, 0, 0, 0 },      //  10 -> 11 is CW
    { 0, 0, 0, 0 }            //  11 -> 01 is silent CW
};

RotaryEncoder * RotaryEncoder::_instance;

RotaryEncoder::RotaryEncoder(int encoderPin1, int encoderPin2, int buttonPin)
 : _encoderPin1(encoderPin1), _encoderPin2(encoderPin2), _buttonPin(buttonPin), _position(0), _min(0), _max(0), _usingmin(0), _usingmax(0), _buttonState(0), _buttonPrevious(0), _time(0) // constructor initializer list
{
    pinMode(encoderPin1, INPUT);
    pinMode(encoderPin2, INPUT);
      pinMode(buttonPin, INPUT);
    digitalWrite(encoderPin1, HIGH);      //  activate internal pullups
    digitalWrite(encoderPin2, HIGH);
      digitalWrite(buttonPin, HIGH);

    _previous = _readEncoderPins();      //  read initial position

    TIMSK2 |= (1 << TOIE2);            //  enable timer 2 overflow interrupt

    _instance = this;
}

inline int RotaryEncoder::_readEncoderPins(void)
{
    return digitalRead(_encoderPin2) << 1 | digitalRead(_encoderPin1);
}

inline int RotaryEncoder::_readButtonPin(void)
{
      return digitalRead(_buttonPin) == HIGH ? 0 : 1;
}

inline void RotaryEncoder::isr(void)
{
      //____________________________________________
      //                                Read Encoder
      int quadbits = _instance->_readEncoderPins();

      if (quadbits != _instance->_previous)
      {
            int position = _instance->_position + _quadrature[_instance->_previous][quadbits];

            //  limit to minimum if assigned
            position = _instance->_usingmin ? max(_instance->_min, position) : position;

            //  limit to maximum if assigned
            _instance->_position = _instance->_usingmax ? min(_instance->_max, position) : position;

            _instance->_previous = quadbits;
      }

      //____________________________________________
      //                                 Read Button
      int reading = _instance->_readButtonPin();
      
      // if we just pressed the button (i.e. the input went from LOW to HIGH),
      // and we've waited long enough since the last press to ignore any noise...  
      if (reading != _instance->_buttonPrevious)// && millis() - _instance->_time > 100)
      {
            _instance->_buttonState = reading;

            // ... and remember when the last button press was
            //_instance->_time = millis();    
      }

      _instance->_buttonPrevious = reading;
}


ISR(TIMER2_OVF_vect)
{
    RotaryEncoder::isr();
}

Ciao Sunny

Cheers for the code!

Can you give an example of a sketch to implement this?

Not sure how I can include this in my loop.

Cheers

Gareth

how you run it:

#include "RotaryEncoder.h"

RotaryEncoder rotary(3, 4, 2);

void setup()
{
  Serial.begin(9600);
  
  rotary.minimum(0);
  rotary.maximum(100);
  rotary.position(20);
}


void loop()
{
  if(rotary.pressed())
  {
    Serial.print("Sensibility: ");
    Serial.print(rotary.position());
    Serial.print("%\n");
    delay(200);
  }
}

Ciao Sunny

i got it working, but did write my "own" little library based on some others.

Nice looking library. Has anyone used it yet and can confirm it works?

Now for extra credit, I'm already using timer2 elsewhere in my code, and there's no way I can get around using it. Anyone have suggestions for modifying this library?

The interval I'm using on timer2 changes over a wide range of possible values, so I can't really share the timer.

What practical application are you using the rotary encoder in? I've got a bunch of them myself, small and good looking, but I cannot figure out a good use for them (actually, I always find a software way to get around using them).

This project will generate precision timing pulses (with a frequency set by the rotary encoder), and the precision part is why I have to use the timer interrupt. It also has to drive a character LCD.

Next project will be almost identical hardware, but it will be in intervalometer for Nikon cameras with the encoder to select time between pictures.

OK, I see.
Now, I think you would need some feedback for the rotary encoder setting anyway, on an LCD, or some flashing light or something. In this case, can't you have just a push button, and handle the setting through software? (That is, push the button until you reach the desired frequency, and when you reach maximum go back to 0).

That is basically what a rotary encoder is except that as you turn the knob it keeps pressing the button for you.

It's a user interface design choice. There is never an instance where you can't replace a rotary encoder with buttons but there's many cases where the enoder is nicer...volume knob on an amp, jog wheel on a cell phone, etc. In this case, I have a range of a couple of hundred values, it will be tedious with buttons, but it's just 10 revolutions of the knob for 200 ticks.

In this case, I need coarse and fine adjustments, the encoder has a built in button (push in the knob), so I can have it work as push for coarse leave out for fine. That's harder to do intuitively with buttons.

I don't know why it makes a difference having an LCD to give feedback. The encoder doesn't have a "zero" position anyway. As long as I turn clockwise the number should increase (until the end condition) and counterclockwise should decrease (until the other end condition). In this case, I'll probably just have the number stay put at the end, not wrap around, or maybe go into a function menu.

OK, I got it. I think I had something a bit different in mind, like this one:
http://parts.digikey.ca/1/1/10411-rotary-dip-switch-bcd-w-shaft-a6a-10rs.html
(this is what I have).

Googling for "rotary encoder" results in wildly different things than the one from the first link on this thread.
I think it should have been called "rotary switch" rather than "rotary encoder" in the first place. I guess that the wording "encoder" comes from the fact that the switch position is returned as BCD (on 4 bits), so one would need a decoder to interface it with a system.

The rotary switch pictured above has a zero position (among 9 others, numbered). You can rotate it both directions and the rotation is not very smooth (wouldn't make a good volume control, from this perspective). The only application I can see for these would be some (mostly) static/predefined settings, like a temperature threshold, or for setting some voltage or frequency level. And since the writing on them is pretty small, I would like to have some feedback, eventually showing a meaningful value rather than a number between 0 to 9.

I think a quadrature rotary encoder with a push button is a very nice and intuitive user interface. Turn encoder to see function options available and press switch to select it. However as others have said a quality rotary encoders seem to be costly and the cheap mechanical one I got is not very precise in it's detents Vs steps.

I read one article about converting a stepping motor to act like a rotary encoder. It required a little external components but sounded like a need project to play with. Some steppers have very nice feeling 'jogs' and with a nice heavy knob might have a very nice feel.

Lefty

Check this out:
http://www.goldmine-elec-products.com/prodinfo.asp?number=G9962
One can turn it manually (like a rotary "encoder", but connected on an analog pin), or remotely (infrared).

OK, I got it. I think I had something a bit different in mind, like this one:

Your link calls this a Rotary DIP Switch, which very accurately describes where you'd use it. Instead of having to toggle binary values on a DIP switch, you can enter the value in decimal with one of those devices. Any sort of configuration setting that's not changed very often.

One can turn it manually (like a rotary "encoder", but connected on an analog pin), or remotely (infrared).

A potentiometer is a very basic electronic component, even radioshack stocks a huge assortment of crappy quality ones. The arduino has 10-bit ADC, so ideally I'd be able to get 1024 different values over 3/4 of a turn of rotation, in practice, these pots aren't very good and have a lot of drift unless I ignore the bottom 2-3 bits. This is useless for my application. I do have some 10-turn pots with very complex knobs that show the value through the 10 rotations that were originally about $300 each and are physically larger than I expect my finished project to be. But they are very impractical for what I'm doing becuase of size and price.

I also have an optical rotary encoder with about 1000 steps/rev that's around the same size and price as the 10 turn pots and gives perfect squarewave outputs. But I can't use it either for the same reason.

A rotary encoder has continuous rotation, which is exactly what I need and are not limited by bits of precision that mean I need a $0.50 device to be stable over 5V/1000 steps = 5mV fluctuations.

If I wanted to use an IR remote, it would be easier to simply take the input from there instead of turning a pot.

Thanks for the useful library, SunboX.

I tried using multiple instances of it, and it didn't work:

RotaryEncoder rotary1(7, 8, 9);
RotaryEncoder rotary2(10, 11, 12);

It is only returning readings from the rotary2 encoder. What needs to be changed in the library so multiple encoders can be used?

Thanks!

I used exactly that code from RotaryEncoder.h, RotaryEncoder.cpp and the example code from "how to run it" in RotaryEncoderTest.pde. I connected the encoderPins pinA to Arduino pin 3, encoderPins pinB to Arduino pin 4, encoderPushButton to Arduino pin 2 and common to ground, but I couldn't get any output from void loop() in RotaryEncoderTest.pde.

Than i included a Serial.println("Start"); statement in the very beginning of void setup() of RotaryEncoderTest.pde. To verify that the RotaryEncoder object gets created I included a Serial.print("Encoder Object created"); message in RotaryEncoder.cpp, which worked fine. But still I didn't get any message from RotaryEncoderTest.pde.

Please, where is my mistake? :-/

gatonero -

Check out Keith Neufeld's 'Quadrature' library. It has solved all my encoder needs. It allows for multiple encoders, works on overflow interrupt, and is easy to send over serial.

Here is his explanation:
http://www.neufeld.newton.ks.us/electronics/?p=248

And here is the library download:
http://www.neufeld.newton.ks.us/electronics/?page_id=249

There are examples, but if you need any more, let me know, and I'll post some code

I testes this also before. But with the example code while compiling, I'm getting this error message:

/usr/lib/gcc/avr/4.3.0/../../../../avr/include/stdlib.h:111: error: expected `)' before 'int'

/usr/lib/gcc/avr/4.3.0/../../../../avr/include/stdlib.h:111: error: expected `)' before 'int'

In file included from /home/christoph/bin/arduino-0015/hardware/cores/arduino/WProgram.h:6,

Couldn't determine program size: avr-size: '/tmp/build18656.tmp/quadrature_two_encoders.hex': No such file

gatonero -

Oh yeah, I thought Keith was going to update. Some libraries have the same problem in version 0013.

Adding this after all your #include statements should clear up the problem:

#undef int                    
#undef abs
#undef double
#undef float
#undef round

Thank You Mark. that works. I will post my further experiences with the arduino-quadratur library.

Very strange! Now the sketch gets compiled without any error messages. But like with the library RotaryEnconder no Serial.print() output. the Quadratur library. :o

My environment: Ubuntu 8.10 64bit, Arduino 0015 alpha