Incremental encoder - help with reading signals

Hello,

I am having 2 incremental encoders with 3 channels - A, B, Z.
After disassembling one of the encoders due to its ball bearings running rough, I got to get a look at the optical disk and as it turns out it happens to have only one signal bar on the ring for the channel Z.

As my engine indicator forces me to find a method to get the rpm and the TDC of the engine, I was about to buy a Hall sensor, which would have to be alligned with the TDC. The rest would have been easy going from there.

But since the encoders themselves contain a third signal ring with only one single bar, I could attach the encoder to the engine in a way, that this bar goes through the photo sensor at the same time as the engine is going though TDC (given: one cylinder). This will make my project much easier in retrospect to the parts.

But here is the challenge: I am trying to get the third channel to send me a signal whenever it travels through the photo resistor, but the serial monitor is only writing down the number that channel represents. And not just when the photo resistor is triggering it.

It sounds like a simple problem. Yet I am having my problems getting to the bottom of it.

I don't know, if the transparent section of the signal ring or the black bar signals a high or low signal. I thought of something like the signal gets transmitted when the pin goes from low to high (in case high beans the bar blocks the light)

but the serial monitor is only writing down the number that channel represents

So you have some code that is reading the encoder and printing to serial monitor? We can only help with code that we can see.

Post a data sheet for the encoder.

Read the how to use this forum-please read sticky to see how to properly post code and some advice on how to ask an effective question. Remove useless white space and format the code with the IDE autoformat tool (crtl-t or Tools, Auto Format) before posting code.

The code is as follows. I consider the exact specifications irrelevant for the question how to get a ping the instant the third ring signals. But here we go:

it is an Eltra EL 40E
1500rpm max
150ppr
5v
NPN mode
3 Channels, Ground and VDC

#include <math.h>
int pin =4;
int n = LOW;
void setup() {
  pinMode (pin, INPUT);  //
  Serial.begin (9600);            // Initializes Serial connection.
}
void loop()
{
  n=digitalRead(pin);
  if(digitalRead(pin) != n)
  {
  Serial.print(digitalRead(pin));
  }
}

So, the idea here is, that if the Arduino reads a high signal on pin 4 and if that signal was low before, I get something written on the serial monitor. But it doesn't seem to correspond with the incremental disk at all. I tried all 3 pins / signal rings, but none puts out 1 signal per rotation. This should be physically impossible.

a serial print takes a bit of time and you're likely to miss encoder events.

i've only used a 2 channel encoder. i connect one output to an interrupt and configure it for the RISING edge. The interrupt simple reads the other input and either increments or decrements a position count.

the other benefit of using the interrupt is the print can be done in loop() and the interrupt can execute without interference

I am actually using an interrupt in my main sketch, which is already correctly calculating the crank angle from the encoder signals, calculating the piston position in relation to that and the power. But the rpm and pressure curve are manually put in.

I don't like the idea of counting the amount of pulses per rotation, as I do suspect, that there could be missed signals and the one-signal-per-rotation thing was supposed to introduce a correcting factor, like:"If this signal shoots off, the encoder is reset to zero."

I now tested all 3 signal outputs and yet every one of them shows lots of changes during one rotation. So, the third disk with one single signal bar is not recognizable in the data output. Ugh, how frustrating. At least, if anything, I can still add a Hall sensor.

not exactly sure what your seeing. developer have told me they used encoders with steppers to more reliably verify position

i saw extra interrupt events that suggested movement in the opposite direction. i decided to ignore event that happened in < 1/2 the time the previous event took. I think there is noise from the long leads and think hysterisis will help. i hope to use a 555 time threshold/trigger

You said you have 2 encoders; is one mounted to the crankshaft and the 2nd to the camshaft?

Assuming you're using this on a 4-stroke engine, are you trying to read the index ('E') from the camshaft encoder to indicate TDC on the compression stroke?

Please post your actual code. Polling is not going to work for this. You need to use an interrupt to capture a rising or falling edge of the index output.

How are things wired now? Because engines only turn one way, you don't need interrupts for both A and B phases of the crankshaft sensor; you should be able to use 'A' or 'B' as one interrupt and the index as the second interrupt.

The indicator is not connected to the engine yet. It still in in "manual rotation mode". It is thought to be working on a steam engine, so the encoder will rotate in the same rpm as the crankshaft. I actually only use one encoder. I just got one for free and I switched them to get any progress, but they are completely identical. So, nothing changes.

I can not post the actual code, because I only add a subroutine to the real sketch, if I finished fixing it.

At least, I can show you the main code part for the encoder:

#include <math.h>

const int encoder0PinA = 3;
const int encoder0PinB = 4;
float encoderping = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() {
  pinMode (encoder0PinA, INPUT);
  pinMode (encoder0PinB, INPUT);
  Serial.begin (9600);
}

void loop()
{
  n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH))
    {
    if (digitalRead(encoder0PinB) == LOW)
      {
        encoderping-=(2.4);
      }
    else
      {
        encoderping+=(2.4);
      }
    encoder0PinALast = n;
    angle = encoderping;
    angle = angle % 360;
  }
    encoder0PinALast = n;
}

What you see, outputs the angle, counting from 0° to 360° and then beginning anew. This is fine and all, but I intend to use the Z channel (only 1 signal per rotation) as:

  1. an easy way to calculate the rpm
  2. a way to allign the shooting-off of this signal with the TDC
  3. a possibility to create a "if channel Z shoots off, everything is reset to 0°"

But remember this doesn't contain the channel Z, which I want to add. But I don't know how, because I don't get channel Z to only announce itself when the photo resistor sees the black bar.

I need it to get me the moment it goes from low to high (or vice versa - you know what I mean)

What about something like this:

//

const byte pinPhaseA = 2;
const byte pinIndex = 3; 

volatile byte ckpSensor;
byte lastCKPSensor;

void setup() 
{
    pinMode( pinPhaseA, INPUT );
    pinMode( pinIndex, INPUT );
    attachInterrupt( digitalPinToInterrupt(pinPhaseA), isrCKP, RISING );
    attachInterrupt( digitalPinToInterrupt(pinIndex), isrCPS, RISING );
    lastCKPSensor = 0;
    ckpSensor = 0;
    
}//setup

void loop() 
{
    if( ckpSensor != lastCKPSensor )
    {
        lastCKPSensor = ckpSensor;
        Serial.println( ckpSensor );
        
    }//if
    
}//loop

void isrCKP( void )
{
    ckpSensor++;
    
}//isrCKP

void isrCPS( void )
{
    ckpSensor = 0;
    
}//isrCPS

If your encoder outputs are open collector NPN, the Z channel will be floating and will pull LOW when triggered, the LOW pulse will be very short and you will have to catch it with an external interrupt pin set to INPUT_PULLUP or use an external 10k pullup resistor. Attach the interrupt as FALLING.

isn

JCA34F:
If your encoder outputs are open collector NPN, the Z channel will be floating and will pull LOW when triggered, the LOW pulse will be very short and you will have to catch it with an external interrupt pin set to INPUT_PULLUP or use an external 10k pullup resistor. Attach the interrupt as FALLING.

isn't this true for all the encoder outputs

The Eltra webpage shows four output types. NPN is distinguished from NPN open collector.

it's not clear if there are different encoders with different outputs or if a particular encoder do not have the same type of output. I can't tell from the Eltra 40 spec sheet

gcjr:
it's not clear if there are different encoders with different outputs or if a particular encoder do not have the same type of output.

The usual way would be to have one type of output per encoder. Note Z isn't listed separately as to an output type selection.

The output is specified in the model number. If the OP posts the full model number we can decode it.

Well, the 2 encoders seem to be of slightly different sub-types:

EL40E 1B150S5N6P1,5
&
EL40E150S5/28N6X3PR1,5 - The 1,5 lies on a fold. So it could also be 1,6. But only these 2 possibilities.

I now have tried a small sketch, which outputs a counter whenever something changes and I made it dependent only of one single signal cable. I even tried all 3 channels.

BUT ALL 3 channels gave an output of 150 pings per rotation, which should be impossible. The outer 2 rings of the incremental disk possess 150 black bars, but the third only one.

I begin to suspect, that either the 3 output cables do not simply correspond to a single incremental ring or that the channels have to be connected some way.

My take at decoding the model number. Used the most complete number but the smaller number looks about the same as far as specs.

EL40E150S5/28N6X3PR1,5

EL40 model]
E mounting style
150 150 PPR
S without zero pulse
5/28 input voltage, 5 to 28V
N NPN output
6 6mm shaft diameter
X Enclosure rating, standard IP54
3 3000 RPM
P cable output (standard length 0,5 m)
R Radial (not sure what that means)
1.5 Special version?

So there is no zero output according to the model number.

By the way, Blackfin,

Unfortunately, your sketch doesn't seem to work.

Yes, one of them doesn't have a zero pulse, but the other has. I found this out this night, reading the data sheet again, but it was too late to write. Let's see. But it IS strange, that there IS a 1-signal-per-rotation ring on that exact encoder, groundFungus, but it is said to have no z channel.

I am going to continue until I cracked the mysterious encoders.

Riccardo001:
By the way, Blackfin,

Unfortunately, your sketch doesn't seem to work.

To help with debugging, a little more specificity would help.

But for now, what about changing the pin mode setup to:

void setup()
{
    pinMode( pinPhaseA, INPUT_PULLUP );
    pinMode( pinIndex, INPUT_PULLUP );
.
.
.

Does that yield different results?

No. Somehow neither the first sketch nor the second addition seems to work. The compilation is successful every time. I tried to add Serial.begin (9600);, but it doesn't seem to yield any differing outcome. It doesn't print anything on the serial monitor. Maybe your sketches work, but I can not verify it due to the empty serial monitor.