Pololu Wheel Encoder Help.

Hi all,

Has anyone every used the Pololu Wheel Encoder with the Arduino. I'm finding their instructions on their website quite confusing.

http://www.pololu.com/docs/0J20/6.n

I was wondering is somone could help me make sense of their instructions as i'm a newbie and really am struggling.

Their wheel encoder basically bas 2 digital outputs, which i need to read and count up every time the value goes Hi.

Hi!

I've bought these encoder wheels and I'm waiting for them. According to all the documents I have, they consist on 2 inputs for each wheel.

Pololu provides a free function library (Arduino compatible!) in

http://www.pololu.com/docs/0J18/16

It's really very simple and it is based on PCInt interrupt handler. The only weak point is that it is limited to 2 encoders. :-/

If you need more information, look at any basic encoder information web page or write me.

Regards,

/me

Hi Suby,

I tried following their example, but no joy. I guess the bit i'm stuck with is that i dont understand how to intialize the pins following thier code.

static void PololuWheelEncoders::init(unsigned char m1a, unsigned char m1b) ???? how does this define the pins you are using.

I'm only using 1 encoder. Would like to see how you do your code.

Hello everybody!

The Pololu library works with two encoders. This means 4 pins to control:

  • encoder 0 / phase A
  • encoder 0 / phase B
  • encoder 1 / phase A
  • encoder 1 / phase B

Once you have installed the PololuWheelEncoder library (available at http://www.pololu.com/docs/0J20/3 ), you can declare your encoder object as

#define pA0 4
#define pB1 5
#define pA1 6
#define pB1 7

PololuWheelEncoders enc;

void setup() {
   enc.init( pA0, pB1, pA1, pB1 );
   // place the rest of your code here !! 
}

There are some issues I do not particularly like such as sharing this library for 2 encoders or limiting the maximum number of encoders in the Arduino. This library also needs start and stop functions to begin and stop counting.

I'm writing my own encoder library. As soon as I test it properly (I hope this week...), I can post it in a website or blog.

Best Regards,

/me

Hi Suby,

I would very much like to try your library once you've tested later this week, as i'm desperate to get this up and running to complete a project i've been working on for a long time.

I really appreciate your help on this.

Rich.

Hey Suby,

Did you manage to get anywhere with your code ?

Rich.

My code looks like this. It’s not tested because I received my pololu encoder wheels yesterday morning. :-[

/*

 Encoder.h - Encoder handling library
 Copyright (c) 2009 SMA.  All rights reserved.
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 The latest version of this library can always be found at
 
 */

#include "pins_arduino.h"
/*
 * an extension to the interrupt support for arduino.
 * add pin change interrupts to the external interrupts, giving a way
 * for users to have interrupts drive off of any pin.
 * Refer to avr-gcc header files, arduino source and atmega datasheet.
 */

/*
 * Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
 * The PCINT corresponding to the pin must be enabled and masked, and
 * an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
 * must use some logic to actually implement a per-pin interrupt service.
 */

/* Pin to interrupt map:
 * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
 * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
 * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
 */

volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = { 
  NULL };

volatile static uint8_t PCintLast[3];

/*
 * attach an interrupt to a specific pin using pin change interrupts.
 * First version only supports CHANGE mode.
 */
void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  if (mode != CHANGE) {
    return;
  }
  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }
  slot = port * 8 + (pin % 8);
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      if (PCintFunc[pin] != NULL) {
        PCintFunc[pin]();
      }
    }
  }
}

class Encoder {
private:
  static uint8_t _pA, _pB;
  static int32_t _position;
  static void service(void);
public:
  // public methods
  Encoder(uint8_t, uint8_t);
  void start();
  void stop();
  void write(int32_t);
  int32_t read(void);
};

uint8_t Encoder::_pA = 0;
uint8_t Encoder::_pB = 0;
int32_t Encoder::_position = 0;

//
// Constructor
//
Encoder::Encoder(uint8_t pA, uint8_t pB) {
  // initialisation of class attributes
  _pA = pA;
  _pB = pB;
  _position = 0;

  // initialisation of I/O
  pinMode( _pA, INPUT );
  pinMode( _pB, INPUT );
  digitalWrite( _pA, HIGH );  // pull-up
  digitalWrite( _pB, HIGH );  // pull-up
}

void Encoder::start() {
  PCattachInterrupt( _pA, service, CHANGE );
}

void Encoder::stop() {
 PCdetachInterrupt(_pA);
}

void Encoder::write(int32_t position) {
  _position = position;
}

int32_t Encoder::read(void) {
  return _position; 
}

void Encoder::service(void) {
  if ((digitalRead(_pA) == LOW) ^ (digitalRead(_pB) == HIGH)) _position++;
  else _position--;
}

I suggest you to have a look at it and have a try. It compiles Ok.

Just copy it to an sketch and call an encoder object like

Encoder enc0(2,4);

void setup() {
  enc0.write(0);
  enc0.start();
}

void loop() {
  delay(1000);
  Serial.print( enc0.read() , DEC);
}

enc0.read() would return a signed 32-bit value. Just rotate manually your wheel !

Tell me something. :wink:

/me

Hi suby,

I created the Encoder.h file. Not sure if i did something wrong, i just cut and past the top box of code you pasted into Encoder.h, but Arduino didn’t like it.

These are the errors it gave :

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:54: error: 'NULL' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h: In function 'void PCattachInterrupt(uint8_t, void (*)(), int)':

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:68: error: 'CHANGE' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h: In function 'void PCint(uint8_t)':

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:130: error: 'NULL' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h: In constructor 'Encoder::Encoder(uint8_t, uint8_t)':

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:165: error: 'INPUT' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:165: error: 'pinMode' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:167: error: 'HIGH' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:167: error: 'digitalWrite' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h: In member function 'void Encoder::start()':

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:172: error: 'CHANGE' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h: In static member function 'static void Encoder::service()':

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:188: error: 'digitalRead' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:188: error: 'LOW' was not declared in this scope

C:\arduino\hardware\libraries\WheelEncoder/Encoder.h:188: error: 'HIGH' was not declared in this scope

Rich.

Hi just look at the library file i noted you had reference to :

include "pins_arduino.h"

Is this a sperate library file you have written thati will need ?

Hi!

Encoder is not yet a library. As soon as I can, it will become a library, but it is still a piece of sketch which includes a class.

I've just created an sketch and then I've added a new tab called Encoder (this will be an "Encoder.pde" file). Then I've placed my encoder code inside this tab. Afterwards I've created another tab called main, which includes setup() and void() procedures.

This works Ok for me.

/me

Hi Thanks for the reply.

I’m still a complete novice at C/Arduino programming, so i’m not sure what you mean by Tabs.

I’ll just wait until you complete your code, and then i can see exactly how you have made it work for you.

Rich.

Hi!

I've checked it and it's OK. There were a couple of bugs, but now it's working fine. ;)

Have a look at it here:

http://sites.google.com/site/freeduinoprojekt/ardubot/pololu-wheel-encoders

You will find a zipped copy of my Arduino sketch, so just open it and adapt the "main" to your needs.

Good Luck!

/me

I'm afraid that there is still an small bug in the Encoder::service function, that makes the pulse count different on each way... :(

Hey Suby,

Do you think you can figure it out ?

I guess if you can't worse case scenario is that i will just get a stepper motor driver and stepper motor to use as a positional counter for my project.

Rich.

Let me a couple of hours. It's just an small bug.

/me

Hey Suby no worries, there’s no hurry. i’ll just keep an eye out on the forum to see if you manage to get it cracked.

Cheers,
Rich.

When debugging with my serial terminal, I get something like this:

  • first column are phase A and B;
  • second column is phase A interrupt counter;
  • third column is phase B interrupt counter;
  • last column is the position counter.
10 1 0 1
00 2 0 1
01 2 1 0
00 2 2 0
10 3 2 1
00 4 2 1
01 4 3 0
00 4 4 0
10 5 4 1
00 6 4 1
01 6 5 0
11 7 5 0
10 7 6 -1
00 8 6 -1
01 8 7 -2
11 9 7 -2
10 9 8 -3
00 10 8 -3
01 10 9 -4
11 11 9 -4
10 11 10 -5
00 12 10 -5

This behaviour is quite odd for an encoder. It doesn't behave as I expected. Let me write this to Pololu!

/me

I suspect it might be seeing something similar to what i saw. Testing their ecoder / wheel configuration i noticed that that when the wheel spokes transited over the sesnors it would cover the first sensor, then both, then the second sensor and repeat the cycle.

I tried re-positioning the sesnors for hours but couldn't get it stop seeing 2 sensors at once.

So my sequence would look something like this e.g.

Forward direction ---> A, A&B, B Reverse direction ----> B, B&A, A

I managed to overcome counting 2 sensors at once with an if statement, but i couldn't figure out a way of stopping the counter counting too many steps during the transit over a sensor

Hey Suby, you had any luck with Pololu ?

I've had a couple of hard work days and I was too tired to write something easy to read and understand. Today I've posted this: http://forum.pololu.com/viewtopic.php?f=3&t=2095

Let's wait for their answer!

Anyway I've found this: http://letsmakerobots.com/node/11987 and http://code.google.com/p/robotfreak/. We can try its code, but I'm afraid that we shall have the same results. I think that there may be a bad trimming in the encoder board potentiometers.

I'll try to get an oscilloscope to have a look at them.

/me