Optimization of incremental encoder

THANK YOU very much ,but i am very bad at coding and microcontrollers so either i will give the code i use ,you please ensure it and if possible teach me how it works

const char encTable[16] = {0, 1, -1, 0, -1, 0, -0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //gives -1, 0 or 1 depending on encoder movement
volatile long encoderCount;
volatile long errorCount;
volatile byte encState;
unsigned long prevDisplay;
unsigned long interval = 0;
int b = 0 ;
int on;
int ir_on = 4;

void setup()
{
  Serial.begin(115200);

  pinMode(8, INPUT_PULLUP ); //output A
  pinMode(9, INPUT_PULLUP ); //output B

  //Enable pin change interrupts on pins 8 and 9 PB 0 and 1
  PCICR |= (1 << PCIE0); //enable group interrupts on PORTB PCINT[7:0]
  PCMSK0 |= (1 << PCINT0); //enable interrupt pin 8
  PCMSK0 |= (1 << PCINT1); //enable interrupt pin 9
}
void loop ()
{
  on = digitalRead(ir_on);


  if (on == LOW)
  {
    b = 1;
    encoderCount = 0;
  }
  if (b == 1)
  {
    if (millis() - prevDisplay >= interval)
    {
      prevDisplay += interval;
      noInterrupts();
      long copyEncoderCount = encoderCount;
      //encoderCount = 0;
      long copyErrorCount = errorCount;
      //errorCount = 0;
      interrupts();

      Serial.print(copyEncoderCount);
      Serial.print('\t');
      Serial.println(copyErrorCount);
    }
  }
}

ISR (PCINT0_vect)
{
  encState = ((encState << 2) | ((PINB) & B00000011)) & B00001111; //use encoder bits and last state to form index
  encoderCount += encTable[encState];//update actual position on encoder movement
  if (encTable[encState] == 0)
    errorCount++;
}

the encoder rpm as per data sheet is :mechanical 5000 rpm
electrical revolution 3000 rpm

then i just compared sampling frequency of both encoder and arduino it was in the limit

User-Manual-Orange-3806-OPTI-600-AB-OC-Rotary-Encoder-ROBU.IN_ (1).pdf (672.6 KB)

What do you mean by "sampling frequency" ? If you are using an interrupt for detecting signals the sampling frequency depends on how long it takes to execute the interrupt routine.
The more commands inside the interrupt-routine the lower the "sampling frequency"

Anyway: counting exact 4096 pulses per revolution is no selfpurpose.

what do you want to do

in the end?

You are trying to fix a detail-problem that might just pop! away if a completely different solution can be used.

here is an analogon that shall show what can happen if just ask for details:

Newbee: "I want to do better cutting please help me sharpening. "
Expert: Sure I can help you. What kind of cutting-tool are you using?
Newbee: a scissor.
Expert: OK take this sharpening tool
Newbee: Yea works great Next question How can I make it cut faster I need to finish faster.
expert: Motorised scissors.
newbee Yea works great though still not fast enough.

expert: Ok can you give an overview about what you are cutting?
newbee: the green of a football-arena.
expert: Oha! take a big mowing tractor with a seven boom spindel-mower and GPS-steering

In the beginning the newbee always just told details.
The expert was assuming the newbee knows that his basic approach is well suited.
which turns out to be very bad suited
that's the reason why it is always a good idea to give an overview and to explain what shall happen in the end.

best regards Stefan

If this is the "purpose" this is a very strange aproach to solve the problem of missing counts.

Optical encoders work very reliably in themselves. If the microcontroller you are using is missing some pulses the reasons are

  • bad signal-quality through too bad wiring (unshielded wires, wire too close to strong electromagnetic fields)

  • slip between motor and rotary-encoder

  • too slow microcontroller

  • too slow programmed code

  • too bad quality of the encoder for this purpose.

From what I have written above the solution is improving the mentioned thing

  • making the mechanical connection between encoder and motor tight enough to avoid any slipping

  • shielded wires

  • move wires away from electromagentic fields

  • faster microcontroller

etc.

but not correcting a counter through a proximity sensor
best regards Stefan

slip between the encoder and the shaft is zero, i have a proper coupling and i can ensure the the wire is shielded and know magnetic because it works perfectly at lower rpm. but i dont know weahter my arduino uno is capable for it ,and i can ensure you encoder is proper .

What rpm is your application?
Why do you need the full 4096 resolution instead of the 2048 half resolution?

Thanks for your advice here I explain my whole project compressed air engine I have a two-stroke engine which I'm modifying it to my application, in order to create efficiency in the engine I have an idea of adding 5 / 3 dcv at top of my engine head it controls air intake and exhaust of air in the engine . My idea is to control the intake of air in the engine by controlling.

the 5/3 DCV WHICH is solenoid operated.

So I like to make a controller that takes input from an optical encoder my engine TDC is detected by proximity sensor as soon as TDC is deducted that encoder knows which is the starting position, otherwise the intake and exhaust position might change because the controller do not know the current position of my crankshaft when switched on. When i give a kick to my engine, the crank will rotate at least once at that time TDC will be detected after getting a signal from the proximity sensor, I'm getting the input from the optical encoder now based on the count range solenoid will on and off. For example, if I program the solenoid to on and of between the count of 5 to 350, the solenoid will on/off properly at a particular angle in every rotation
so if I like to change it intake period I will just change number 350 to 400 or something and re-upload the program and run. this will make change in running of my engine and in future i will add things like change the value of intake using the potentiometer which acts like throttle to my engine, but for doing this i need my optical encoder to detect angle properly with mint program and I'm ready to go for alternative microcontroller if you suggest me best

rpm is 600 to max 1000 rpm,
high resolution because if a big count is missed like 50 numbers it will make only a small change in output

1000 rpm is 16.7 rps. and with 4096 counts/rev you have 68,400 counts/second or about 14.6 microseconds between counts.

Depending on what else is going on in the program, this is not exactly a comfortable interval.

Given the application you describe, I believe that you should use the half resolution 2048 counts/rev. The encoder should not miss counts.

That's still 360/2048 = .176 degree per count. I can believe that the timing accuracy you require will be that high. Variation of the proximity sensor for tdc and the physical opening and closing of the solenoid valve will create more timing issues than the encoder at 2048 resolution.

If you really do require the accuracy of the 4096 resolution you may wind up needing a faster processor. You really need to get started, and see what real world problems come up.

I believe that typically in an engine timing application people work with a time from the tdc trigger signal and not encoder counts. Engine rpm is not changing that fast, and when you determine rpm you can work out the delay/advance timing.

There are microcontrollers that offer hardware-support for quadrature-encoderrs internally in the microcontroller. Teensy 4.0 and Tensy 4.1.

I haven't any experience with it. But as the Teensy 4.0/4.1 have a 600 MHz Clock and hardware quadrature encoder-support I'm pretty sure that they can do the job.

You can download teensy board suppportt from the manufacturer PJRC#s website
best regards Stefan

thank you guys for your support now from your advices i come to a conclusion instaed of running my engine at higher rpm of 600 to 1000 let me run it from 100 to 300 rpm and prove my concept .
Then i will improve the microcontroller to teensy 4.0 or any excellent alternative and run my engine from 600 to 1000 rpm. So with my hardwares in my hand can you guys say running the engine in which rpm is good and and effective.

This is an excellent idea. I'm certain there are many issues to work out and starting slower, and with lower air pressure is likely a safer route.

From what I can read online about compressed air engines your positional timing with 1024 or 2048 counts per revolution will likely be adequate for your valve timing.

I would even think that the z axis output pulse from the encoder will possibly be more accurate than the inductive proximity sensor for triggering the valve timing cycle. You will read the z axis pulse with an interrupt and it will be fast.

I have my concerns with the inductive proximity sensor. From the spec sheet, it needs to be very close (4mm/20) to work at 1000 Hz, and vibration might be an issue

You may want to explore alternative methods. Perhaps you can mount a small magnet for a high speed hall sensor, or use potentially faster optical methods.

This compressed air engine is very interesting. What different parameters effecting efficiency do you plan to study? Is it for a university course or final project? Please keep us posted.

thank you for suggestion yes this is for my university project . i will need your help throug out the project please be with me , i like to mention all your names when i submit it at my university.

Are you working with any of the people who wrote this paper?
https://www.ijrer.org/ijrer/index.php/ijrer/article/view/11326/pdf

NOT with them but the concept is almost same,thanks for the wonder full paper

learned something new. Does this mean if an encoder has a Z-output this output is triggered each full revolution indicating exactly that "revolution finished?

I'm pretty sure nothing can be faster that and more acurate than this Z-pulse.
Any other electronics needs to detect a change with additional electronics that has an additional propagation-delay.

Only disdavantage the Z-pulse will have an offset to a particular engine-position which will vary after dismounting/re-mounting the encoder. But this offset could be determined by
some components mounted on the engine-shaft
(like small magnet / hall-sensor / fork-light-barrier with a single small slot etc.)
to give a signal THIS position reached
slowly rotating the engine-shaft
if shaft-sensor signal occurs counting encoder-pulses until the Z-pulse occurs

best regards Stefan

I LIKE THE IDEA OF USING Z PULSE BUT THE PROBLEM IS

  1. Z PULSE MUST BE EXACTLY AT TDC OF MY ENGINE EVEN IF I CAN DO IT BY MOUNTING IT AT CERTAIN AINGLE

  2. IF DUE TO SOME VIBRTATION OR DISTURBANCE ON ENCODER THE Z PULSE MAY VARY SO THE ERROR OCCURES ,AND IT WILL NOT CHANGE AT NEXT REVOLUTION

BUT,if i use a proximity sensor external, I'm checking the TDC position properly,and the proximity sensor will delete the error made by encoder at next position

It does not have to be aligned with TDC as I have described above a proximity sensor not as every revolution-correction but just for determining the offset between TDC and Z-Pulse after tight re-mounting the rotary-encoder

if this really should happen the components used are not able to withstand the loads occuring while running the engine.

Your optical encoder is a closed housing with components that are detecting pulses optically. Everything inside is mounted to hold a lifetime. Except the rotating part everything can be mounted rock-solid.

A proximity sensor needs to be mounted using screws and threads very close 2mm to another object again mounted with screws and threads which all can go loose.

I don't see where there is an advantage in using a proximity sensor.
nontheless maybe I'm overlooking something important. If so you would have to point me straight forward to it.

IMHO: trying to correct such an error is like using adhesive tape to repair a steel-bridge that requires welding the parts instead of using adhesive tape.

best regards Stefan

ok then , let me try it practically the follwing is the code i am using that has all the parameters i need
in hardware side, let me remove proximity sensor output from pin 4 and insert z -pulse wire that is yellow wire into the pin 4 ,please edit this code as it will use z pulse to reset .

const char encTable[16] = {0, 1, -1, 0, -1, 0, -0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //gives -1, 0 or 1 depending on encoder movement
volatile long encoderCount;
volatile long errorCount;
volatile byte encState;
unsigned long prevDisplay;
unsigned long interval = 0;
int b = 0 ;
int on;
int ir_on = 4;
int led1 = 13;//sol 1-intake
int led2 = 12;//sol 2-exhaust

void setup()
{
  Serial.begin(115200);

  pinMode(8, INPUT_PULLUP ); //output A
  pinMode(9, INPUT_PULLUP ); //output B
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);

  //Enable pin change interrupts on pins 8 and 9 PB 0 and 1
  PCICR |= (1 << PCIE0); //enable group interrupts on PORTB PCINT[7:0]
  PCMSK0 |= (1 << PCINT0); //enable interrupt pin 8
  PCMSK0 |= (1 << PCINT1); //enable interrupt pin 9
}
void loop ()
{
  on = digitalRead(ir_on);


  if (on == LOW)
  {
    b = 1;
    encoderCount = 0;


  }


  if (b == 1)
  {
    if (millis() - prevDisplay >= interval)
    {
      prevDisplay += interval;
      noInterrupts();
      long copyEncoderCount = encoderCount;
      //encoderCount = 0;
      //long copyErrorCount = errorCount;
      //errorCount = 0;
      interrupts();

      Serial.print(copyEncoderCount);
      Serial.print('\n');
      if (copyEncoderCount == 0)
      {
        digitalWrite(led1, LOW);
        digitalWrite(led2, LOW);
      }
      else if ((copyEncoderCount >= 38 ) && (copyEncoderCount <= 335))
      {
        digitalWrite(led1, HIGH);

      }
      else if ((copyEncoderCount >= 2046 ) && (copyEncoderCount < 5000 ))
      {
        digitalWrite(led2, HIGH);

      }
      else
      {
        digitalWrite(led1, LOW);
        digitalWrite(led2, LOW);
      }


      //Serial.println(copyErrorCount);
    }
  }
}

ISR (PCINT0_vect)
{
  encState = ((encState << 2) | ((PINB) & B00000011)) & B00001111; //use encoder bits and last state to form index
  encoderCount += encTable[encState];//update actual position on encoder movement
  if (encTable[encState] == 0)
    errorCount++;


}

and i think as you said using z axis will eliminate proximity sensor delay which may delet the erroe of 4025 to 4037 at end of every revolution

can you guys please edit the above code for for using the z pulse to reset the count