Using pins 2 and 3 on Arduino Uno for motor encoders

I am using an Arduino Uno R3 to control four motors with built-in encoders. At the moment, I am just doing some basic tests and I am having trouble with reading the encoders from pins 2 and 3. The problem is that they only read 0, -1, or +1 when I manually rotate the motor, while the other digital input pins give me integer readings proportional to the motor position as I would expect. Because I am using several motors, I don't really have spare pins I can use instead, so I would like to get these working. I am an Arduino newbie, so it's possibly I am making a silly mistake somewhere.

My hardware:

The relevant pin connections are as follows. Pins A2 and A3 are connected to the DRV8825 motor driver to control the motor direction and steps. Arduino GND and +5 V are powering the logic of the DRV8825 and also power the encoders. Encoder A+ is connected to pin 2 and B+ is connected to pin 3.

Here is the connection diagram for one motor ("motor 2" below). The others have been left off for clarity.

Here is my connection diagram for a single motor encoder only (other motors disconnected, DRV8825 disconnected):

I have tried connecting different encoders to different pins and verified that the problem is with the pins and not a fault in the encoder, as all four motor encoders have the same behaviour.

My test code is below. The code loops through the four motors and reads the encoder value. Motor 2 is using pins 2 and 3. Note that in the above schematics, I am only showing one motor for clarity, but the pin behaviour is the same regardless if one motor is connected or several. Note that the DRV8825 is disconnected for this test, so I am working with the encoders and Arduino only.

// Tests the encoders. Leave motor power supply disconnected and run Arduino only. Turn the motor and look at the encoder values on the Serial Monitor.

#include <Encoder.h>

const int encPin1[4] = {11, 9, 3, A0};
const int encPin2[4] = {10, 8, 2, A1};
long encPos[] = {-999, -999, -999, -999};
Encoder encoders[] = {Encoder(encPin1[0], encPin2[0]), Encoder(encPin1[1], encPin2[1]), Encoder(encPin1[2], encPin2[2]), Encoder(encPin1[3], encPin2[3])};

//Setup the serial connection
void setup() {
  Serial.begin(115200);
  Serial.println("Motor encoder test: ");
}

void loop() {

  long newPos;

  for (int i = 0; i < 4; i++) {
  
    newPos = encoders[i].read();

    if (newPos != encPos[i]) {
      Serial.print("Motor ");
      Serial.print(i);
      Serial.print(" = ");
      Serial.print(newPos);
      Serial.println();
    }
  }

}

This is an example of my output in the Serial Monitor when I randomly rotate the knobs:

Motor 0 = 90
Motor 1 = 14
Motor 2 = 0
Motor 3 = -35

Motor 2 is reading 0, but it can also be -1 or 1. As you can see, motors 0, 1, and 3 give higher values, with the sign indicating if the motor has been turned clockwise or counter-clockwise.

Any ideas about what's going on? I have tried setting the pinmode of pins 2 and 3 to INPUT and INPUT_PULLUP, but the behaviour was the same. I understand that pins 2 and 3 are interrupt pins on Arduino Uno, so does this mean I have to handle them differently in the code?

Please post the link to the datasheet of the device You use. Sales sites covering the entire family is no good.
Please post schematics. Poetry is not accepted.

1 Like

As @Railroader says -

The manufacturer only supplies a series datasheet rather than for individual models. I have updated the link in my original post to go straight to the datasheet PDF. My motor model is NEMA17-13-04PD-AMT112S.

I have also added schematics.

I've added schematics now.

The encoder is incremental, giving +1 or -1. You need an integer variable keeping the position. That variable is updated by -1, 0 or +1.
I've never used the encoder library....
The usual way is to remember the number of steps sent and using that variable as stepper position. Using an adquate stepper having the torqe needed encoders are not needed. A home switch telling the home position is often used.

I have created the Interrupts library which can be found here: ZHomeSlice/Arduino_Interrupts
I have created an encoder script that allows for 4 steps per pulse mode. Here is your code modified to handle my library:
Note this uses lambda functions and callbacks a lot! hope this helps.

// Tests the encoders. Leave the motor power supply disconnected and run Arduino only. Turn the motor and look at the encoder values on the Serial Monitor.
#include "Interrupts.h"
InterruptsClass Interrupt;

const int encPin1[4] = {11, 9, 3, A0};
const int encPin2[4] = {10, 8, 2, A1};
volatile long Pos[] = { -999, -999, -999, -999};

//Setup the serial connection
void setup() {
  Serial.begin(115200);
  Serial.println("Motor encoder test: ");
  Interrupt.onInterrupt([](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    sei(); // re enable other interrupts at this point,
    Interrupt.PinCallBack(Time, PinsChanged, Pins);
  });
  Interrupt.onPin(10, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[0] += Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(11, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[0] -= Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(9, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[1] += Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(8, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[1] -= Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(3, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[2] += Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(2, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[2] -= Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(A0, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[3] += Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(A1, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[3] -= Interrupt.Encoder(12, 13, Pins, true);
  });
}

void loop() {
  int newPos = 0;
  static int oldPos[4] = { -999, -999, -999, -999};;
  static unsigned long ExactTimer;
  if ( millis() - ExactTimer >= (100)) {
    ExactTimer += (100);
    for (int i = 0; i < 4; i++) {
      newPos = Pos[i];
      if (oldPos[i] != newPos) {
        oldPos[i] = newPos;
        Serial.print("Motor ");
        Serial.print(i);
        Serial.print(" = ");
        Serial.print(newPos );
        Serial.println();
      }
    }
  }
}

to convert from every rise and fall of both the (A) and (B) switches causing a count to just the rise of the (A) Pin change:

  Interrupt.onPin(10, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[0] += Interrupt.Encoder(12, 13, Pins, true);
  });
  Interrupt.onPin(11, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[0] -= Interrupt.Encoder(12, 13, Pins, true);
  });

// to 
  Interrupt.onPin(10, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    Pos[0] += Interrupt.Encoder(12, 13, Pins, false);
  });
//  Interrupt.onPin(11, INPUT, [](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
//    Pos[0] -= Interrupt.Encoder(12, 13, Pins, true);
//  });

Z

Encoder.h is a solid library. It feels to me like one of the two pins is defective and is stuck HIGH or LOW.

Can you run a simple digitalRead() test on pins 2 and 3 to confirm they are working correctly.

1 Like

As I understand it, the Encoder library keeps track of the position for you so you don't have to keep count yourself. You can look at the documentation for the library yourself here. In the example, the encoder count increments when the knob is manually turned, and this is displayed in the Serial Monitor. My code is based on the example code in the documentation. And it works like in the example when I use other pins, but not 2 and 3.

I am not sure why you are bothering to answer if you aren't familiar with the library?

The original setup had motors with no encoders, but we were having trouble with the motors losing their place in some circumstances. We have considered a home switch, but our setup needs to be light-tight at all wavelengths, and most switches of that sort are optical. Also we would possibly need to make some modifications to a metal enclosure we are using. Ultimately, it was easier to swap in new motors with encoders built in. Believe it or not, we have thought this through :slight_smile:

I'll quit Your thread.

1 Like

Testing the pins was a good call! I should have done that first, but I did not suspect that there were dead pins.

Pin 3 is totally dead (does not read inputs and does not output).
Pin 4 reads inputs, but does not work as an output.

I will replace the Atmega microcontroller and have another go. Thank you for the advice!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.