Rotary encoder for small engine dyno rpm

I am new to this and have coding but my rpm are way off. I get 22000 (approx) when the motor is only turn 900 (approx).
I have a e6b2-cwz6c 360 p/r rotary encoder

What am I not doing?

The coding I am using is
const int encoder_a = 2; // Pin 2
const int encoder_b = 3; // Pin 3
long encoder_pulse_counter = 0;
long direction = 1;

void encoderPinChangeA()
{
encoder_pulse_counter += 1;
direction = digitalRead(encoder_a) == digitalRead(encoder_b) ? -1 : 1;
}

void encoderPinChangeB()
{
encoder_pulse_counter += 1;
direction = digitalRead(encoder_a) != digitalRead(encoder_b) ? -1 : 1;
}

void setup()
{
Serial.begin(9600);
pinMode(encoder_a, INPUT_PULLUP);
pinMode(encoder_b, INPUT_PULLUP);
attachInterrupt(0, encoderPinChangeA, CHANGE);
attachInterrupt(1, encoderPinChangeB, CHANGE);
}

void loop()
{
long speed = encoder_pulse_counter;
Serial.print("RPM: ");
Serial.println(direction*speed);
encoder_pulse_counter = 0; // Clear variable just before counting again
delay(1000);
}

I moved your topic to an appropriate forum category @racedad400.

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

320?

Sorry should be 360 p/r

The specs on that encoder show that there are Phases A, B, and Z.
The Z output is one pulse per revolution, and your should be reading that for rpm instead of the A/B outputs.

1 Like
const int encoder_a = 2; // Pin 2
const int encoder_b = 3; // Pin 3
volatile int encoder_pulse_counter = 0;
volatile bool direction = false;
volatile uint32_t lastMicro = 0;
volatile uint32_t pulseLength = 0;

void encoderPinChangeA() {
  if (!encoder_pulse_counter)lastMicro = micros();
  encoder_pulse_counter += 1;
  if (encoder_pulse_counter == 360) {
    pulseLength = micros() - lastMicro;
    encoder_pulse_counter = 0;
  }
  direction = digitalRead(encoder_a) == digitalRead(encoder_b);
}

void setup() {
  Serial.begin(115200);
  pinMode(encoder_a, INPUT_PULLUP);
  pinMode(encoder_b, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), encoderPinChangeA, RISING);
}

void loop() {
  static uint32_t temp = 0;
  noInterrupts();
  temp = pulseLength;
  interrupts();
  Serial.print("RPM: ");
  if (direction)Serial.print('-');
  Serial.println( 60000000 / temp);
  delay(1000);
}

Your encoder puts out 360 pulses per revolution. You are counting pulses on both phases. In fact you are counting changes caused by the pulses on each phase. In one second the engine will make 15 revolutions.

15 revolutions X 360 pulses/rev X 2 phases X 2 changes/pulse = 21,600.

So congratulations your circuit and code are working perfectly.

encoderPinChangeA, RAISE); // ???
Leo..

1 Like

oops, correct is RISING

Firstly, as code provided by @kolaha , you need to use volatile for variables used inside interrupt handler.

Secondly, you can try the code WITH and WITHOUT interrupt in this arduino rotary encoder tutorial and compare with your code to see the differences

If you had provided a link we would know what sensor you are using.

I placed a link to the encoder I am using in the original post.

bitherder_57, that makes some sense as I noticed that when I was doing some initial coding that counted position, turning the encoder almost half a turn would give me 360.
I believe the delay in the last line may also be an issue as the motor's rpm is not constant. Changing the delay longer or shorter will change the rpm. If I set the rpm on the motor so it is constant, adjusting the delay I can get it almost correct, but once I speed the motor up I get what looks like drift in in the rpm reading.

The delay function uses one of the timers on the microcontroller and is based on the clock frequency of the microcontroller. There can be significant variation from one Arduino to the next. The function is at best an approximation. In order to get better accuracy you need a more accurate time base.

Another option is to “calibrate” your time base. You would do this by running the motor at a speed near the lower end of it’s speed range and at the upper end of it’s speed range. Take the raw counts at each of those points and calculate a correction factor (slope) for the RPM. Of course you will need to have a way to accurately measure the actual RPM, which it sounds like you do.

Then it is just a matter of performing the calculations to convert your raw counts to RPM.

Hope this helps.

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