Pulse Counter - Rotary Encoder - PLEASE HELP

I am working on a project that uses a 3-pin rotary shaft encoder. This encoder monitors the position of a linear actuator (OS Series actuator manufactured by Firgelli Automations), and is supposed to return about 50 pulses per inch.

I have never worked with a rotary encoder on an Arduino before and I am struggling to figure out how to count the pulses.

So far I have tried using a digitalRead function to get raw data from the encoder, but have not been able to isolate a pulse.

If anybody has worked with encoders before and knows anything that may help, a reply will be much appreciated.

What do you mean you can't find a pulse? There are a lot of different levels of off you might be from completely clueless to small programming error. You gotta help us know what you need. Post your code. Describe your output. Describe how it should be different. That sort of thing.

If you're just clueless, there is a page on this site about encoders. Google "Arduino Encoder" and have at. The "State Change Example" will show you how to count edges. That's all counting pulses really is.

If the encoder is running too fast you may need an interrupt to catch it. But try it the normal way first and see what you get.

Do you have one of these?

Looks like it is not an encoder, just a pulse generator. You supply +5V and Ground an you get pulses out of the third wire.

“The OS series is equipped with a 10 hole optical encoder disk that with the gearing to the actuator output shaft will send 50 pulses/inch of stroke for the 35lbf unit and 100 pulses/inch of stroke for the 200lbf and 400lbf units. ( +/- 5 pulses).”

You know which direction you are driving the motor so by counting pulses and adding or subtracting based on direction you can roughly keep track of position. Remember to keep counting pulses after you turn off the motor since there will be some overshoot.

Post a link to data about the actual encoder you are using. Show how you have wired it up. Post your complete sketch using the </> code tag button.

Yes it's one of those

johnwasser: Do you have one of these? https://www.firgelliauto.com/products/os-series

Looks like it is not an encoder, just a pulse generator. You supply +5V and Ground an you get pulses out of the third wire.

"The OS series is equipped with a 10 hole optical encoder disk that with the gearing to the actuator output shaft will send 50 pulses/inch of stroke for the 35lbf unit and 100 pulses/inch of stroke for the 200lbf and 400lbf units. ( +/- 5 pulses)."

You know which direction you are driving the motor so by counting pulses and adding or subtracting based on direction you can roughly keep track of position. Remember to keep counting pulses after you turn off the motor since there will be some overshoot.

I wired it up just like that. Outer pin to 5v and GND. Middle pin connected to digital input. I tried to do a code with a digital read output to the serial monitor. But I'm getting nothing but useless bouncy readings. I'm trying something different right now, and will post my code in a little bit.

spyguy518: I tried to do a code with a digital read output to the serial monitor. But I'm getting nothing but useless bouncy readings.

At 50 pulses per revolution it seems likely that the pulses are happening faster than you can read the serial monitor. You will have to avoid delays in your code to sample fast enough to catch all of the pulses.

johnwasser:
At 50 pulses per revolution it seems likely that the pulses are happening faster than you can read the serial monitor. You will have to avoid delays in your code to sample fast enough to catch all of the pulses.

I found this code online that reads from an encoder with 2 signal outputs (for determining direction).

How could this be modified to work for an encoder with only one output?

/* Rotary encoder read example */
#define ENC_A 14
#define ENC_B 15
#define ENC_PORT PINC
 
void setup()
{
  /* Setup encoder pins as inputs */
  pinMode(ENC_A, INPUT_PULLUP);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT_PULLUP);
  digitalWrite(ENC_B, HIGH);
  Serial.begin (115200);
  Serial.println("Start");
}
 
void loop()
{
 static uint16_t counter = 0;      //this variable will be changed by encoder input
 int16_t tmpdata;
 /**/
  tmpdata = read_encoder();
  if( tmpdata ) {
    Serial.print("Counter value: ");
    Serial.println(counter, DEC);
    counter += tmpdata;
  }
}
 
/* returns change in encoder state (-1,0,1) */
int16_t read_encoder()
{
  static int16_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static uint16_t old_AB = 0;
  
  old_AB <<= 2;                   //remember previous state
  old_AB |= ( ENC_PORT & 0x03 );  //add current state
  return ( enc_states[( old_AB & 0x0f )]);
}

Code found here.

You wouldn't. Reading from the one with only one output is much easier. It won't look anything like that.

See the "State Change Example". It shows a great example of how to do edge detection to count pulses. It is counting pulses from a push button, but it might as well be counting pulses from your device. Let it count up or down depending on which way you are driving the motor.

I wired it up just like that. Outer pin to 5v and GND. Middle pin connected to digital input. I

Is the input pin configured for INPUT_PULLUP? Are you using an external pullup resisitor on the output?

Here is a very basic pulse counter using an interrupt on Pin 2.

volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 1000;//one second

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");
  
  pinMode(2,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), isrCount, RISING); //interrupt signal to pin 2
}

void loop()
{
  if (millis() - lastRead >= interval) //read interrupt count every second
  {
    lastRead  += interval;
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();

    Serial.println(copyCount);

  }
}

void isrCount()
{
  count++;
}

if you have an Arduino Due there is a library tc_lib which has functions for measuring pulse widths, period, frequency, etc

If you look at the webpage that John Wasser linked too, then on the 'WIRING DIAGRAM' tab it cleary mentions:

"External 10-15kohm pullup resistor or if using Arduino set pinmode INPUT_PULLUP"

JohnLincoln: If you look at the webpage that John Wasser linked too, then on the 'WIRING DIAGRAM' tab it cleary mentions:

"External 10-15kohm pullup resistor or if using Arduino set pinmode INPUT_PULLUP"

It is set to input_pullup

I am going to try the code that cattledog posted in a little bit

cattledog: Is the input pin configured for INPUT_PULLUP? Are you using an external pullup resisitor on the output?

Here is a very basic pulse counter using an interrupt on Pin 2.

volatile unsigned long  count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0; unsigned long interval = 1000;//one second

void setup() {   Serial.begin(115200);   Serial.println("start...");     pinMode(2,INPUT_PULLUP);   attachInterrupt(digitalPinToInterrupt(2), isrCount, RISING); //interrupt signal to pin 2 }

void loop() {   if (millis() - lastRead >= interval) //read interrupt count every second   {     lastRead  += interval;     // disable interrupts,make copy of count,reenable interrupts     noInterrupts();     copyCount = count;     count = 0;     interrupts();

    Serial.println(copyCount);

  } }

void isrCount() {   count++; }

cattledog, I noticed that you created the function isrcount. But it is never called. What is it for?

cattledog, I noticed that you created the function isrcount. But it is never called. What is it for?

It is the “interrupt service routine”, and it is the code called each time the interrupt is triggered by the rising pulse edge on pin 2. It is code called by the interrupt.

See Gammon Forum : Electronics : Microprocessors : Interrupts

Also, I'm doing this project on a MEGA 2560. Does anybody know which pins are the interrupt capable pins?

spyguy518: Also, I'm doing this project on a MEGA 2560. Does anybody know which pins are the interrupt capable pins?

See the built-in help: Help->Reference->attachInterrupt

Or see the online reference: https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

I got the pulse counter work. I am now trying to use the encoder counts as a variable to actually control the actuator using an L298N Motor Controller, using the attached code. It is working intermittently. And the actuator never completely stops. Just slows down after the variable “pulses” > 112.

Can anybody see anything programmatical that may be causing this?

//pulse count variables
volatile unsigned long  count = 0;
unsigned long copyCount = 0;
unsigned long lastRead = 0;
unsigned long interval = 250;//one second
volatile int pulses = 0;


//other variables

// motor one
int motorA = 10;
int in1 = 9;
int in2 = 8;


void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), countISR, RISING); //interrupt signal to pin 2
  pulses = 0;
  count = 0;
 
}

void countISR() {
  count++;
}


void pulseCount() { //code to read encoder pulses... about 56 per inch
  
  if (millis() - lastRead >= interval) //read interrupt count every 250ms
  {
    lastRead  += interval;
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
   // count = 0;
    interrupts();

    //Serial.println(count);
    delay(5); //buffer


    pulses = copyCount;
    return pulses;

  }

}

void loop() { // main loop
 // delay(5000);  //delay for debugging
  pulseCount();
  //Serial.println(pulses);
   pinMode(in1,HIGH);
   pinMode(in2,LOW);

   if(pulses < 112){ //extend about 2 inches
    delay(20);
    analogWrite(motorA, 255);
    //Serial.println("GO");
    Serial.println(pulses);
   }
   else{  //then turn off
    analogWrite(motorA, 0);
   }
  
}

Your handling of the motor pins is not correct

// pinMode(in1,HIGH);
  digitalWrite(in1,HIGH);
  // pinMode(in2,LOW);
  digitalWrite(in2,LOW);

pulseCount() is a void function and does not return pulses, but pulses is declared as a global variable you are indeed accessing it. pulses does not need to be volatile.

I have tested your code with a pulse generator, and indeed it does stop at 112 pulses, and I believe your issues are with the motor control.