Running 2 encoders in seperate loops

Hi all

I have a project for my lathe that uses linear scales for measurement
Glass linear scale - 5um (0.005mm)
Both scales need to be able to operate independent, with "Zero" set function and "Increment" set function

I can get 1 to work perfectly, with high precision and speed using encoder.h library

But how can I get both to work as the loop/voids rely on buttons ( from screen or physical ) to set the zero point. A button press basically takes it to a new loop specific for that scale

The code so far only includes the X_scale, but the Y_scale will be exactly the same, but labelled Y_****

#include <Encoder.h> // Uses 5um (0.005mm) incremental glass scale linear encoder https://www.vevor.com.au/linear-scale-c_10122/250mm-linear-scale-for-milling-lathe-machine-drilling-3m-signal-cable-precision-p_010844850404

Encoder scaleX(2, 4); // Spread interupt pins 2 & 3 accross each scale, 
Encoder scaleY(3, 5); // res future use

const int X_ZincPin = 6; 
const int X_ZeroPin =7; 

int X_ZeroPinState = 0; // used in loop() to switch to zero() to set zero point for zero measurment when read head set, regardless of intitial power-up position of read head 
int X_ZincPinState = 0; // used to creat incremental value from zero when read head is set to an incremental point. 

long X_ref1 = 0; // reference value
long X_ref2 = 0; // reference value
long X_zeropos = 0;
long X_Zinc = 0;
long X_Zero = 0;
long X_oldPosition1  = 0;
long X_oldPosition2  = 0;
long X_oldPosition3 = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Zero set button:");
  pinMode(X_ZeroPin, INPUT_PULLUP);
  pinMode(X_ZincPin, INPUT_PULLUP);
  }

void loop() {
  X_ZeroPinState = digitalRead(X_ZeroPin);
     if (X_ZeroPinState == CHANGE) {
      
     if (newPosition1 != X_oldPosition1) 
         X_oldPosition1 = newPosition1;

  //long newPosition2 = scaleY.read();
  //  if (newPosition2 != X_oldPosition2) 
  //  X_oldPosition2 = newPosition2;
    
    X-ref1 = newPosition1;
    X_ref2 = newPosition2;
    Serial.print(ref1/200.00,3),Serial.print("mm X-Slide : "),
    Serial.print(" ---ZeroPinstate = "),Serial.println(X_ZeroPinState);
     }
  else {
      X_zero();
         }
}
void X_zero() {

    X_ZincPinState = digitalRead(X_ZincPin);
     if (X_ZincPinState == CHANGE)
      {
        long newPosition3 = scaleX.read();
        (newPosition3 != X_oldPosition3) ;
         X_oldPosition3 = newPosition3;
         X_Zinc = (newPosition3-X_ref1);
          Serial.print(X_Zinc/200.00,3),Serial.print("  =Zinc=  :");
          Serial.print(X_ref1/200.00,3),Serial.print("  =ref1= : ");
          Serial.print(newPosition3/200.00,3),Serial.print("  ==newpos3==  ; ");
          Serial.print(scaleX.read()/200.00,3), Serial.print("  ==scale1==  ; ");
          Serial.print("=ZincPinstate "),Serial.print(X_ZincPinState),Serial.println( " ~~ void zero() ");
          }

     else {
      zinc();
    }
     }
     
void zinc()
{
    long newPosition3 = scaleX.read();
        (newPosition3 != X_oldPosition3); 
         X_oldPosition3 = newPosition3;
         X_zeropos = newPosition3-ref1;
         Zero = (X_Zinc+X_ref1)-newPosition3;
         Serial.print(X_zeropos/200.00,3),Serial.print("  Zero Posn mm : ");
         Serial.print(X_Zero/200.00,3),Serial.print(" Increment Posn mm : ");
         Serial.print(" ZincPinstate = "),Serial.print(X_ZincPinState), Serial.println(" ~+~ void zero() ");

         }
     //delay(1000)

I suggest you format your sketch by pressing CTRL-T to make it better readable.

Your posted sketch is incomplete and doesn't compile:

C:\Daten\myrepository\Arduino\Forum no SVN\sketch_aug02a\sketch_aug02a.ino: In function 'void loop()':
sketch_aug02a:32:9: error: 'newPosition1' was not declared in this scope
     if (newPosition1 != X_oldPosition1)
         ^~~~~~~~~~~~

currently it doesn't seem that you block your code with delays or while loops, so I guesss a second encoder will work in your code.

I just suggest that you avoid to duplicate code for the second encoder. Use arrays and write code only once and let it run two times (for two encoders).
Most of the variables you have declared starting with X_ could be such arrays for for each axis. I would even combine them to one structure and make an array of that structure with these "member" variables. And if you have already a structure, it also can hold the encoder object ...

edit:
some starting point:

#include <Encoder.h> // Uses 5um (0.005mm) incremental glass scale linear encoder https://www.vevor.com.au/linear-scale-c_10122/250mm-linear-scale-for-milling-lathe-machine-drilling-3m-signal-cable-precision-p_010844850404

// a structure to hold
struct Axis {
  const uint8_t zincPin;
  const uint8_t zeroPin;
  Encoder scale;
  int zeroPinState = 0; // used in loop() to switch to zero() to set zero point for zero measurment when read head set, regardless of intitial power-up position of read head
  int zincPinState = 0; // used to creat incremental value from zero when read head is set to an incremental point.
  long ref1 = 0;        // a bad idea to name variables 1, 2, -  either name it or consider to use an array
  long ref2 = 0;
  long zeropos = 0;
  long inc = 0;
  long ero = 0;
  long oldPosition1 = 0; // a bad idea to name variables 1, 2, 3 -  either name it or consider to use an array
  long oldPosition2 = 0;
  long oldPosition3 = 0;
  Axis (uint8_t encA, uint8_t encB, uint8_t zincPin, uint8_t zeroPin) : zincPin(zincPin), zeroPin(zeroPin), scale(encA, encB) {}
};

Axis axis[2] {
  {2, 4, 6, 7},
  {3, 5, 8, 9}
};

constexpr uint8_t x = 0;
constexpr uint8_t y = 1;

void setup() {
  Serial.begin(115200);
  for (auto &a : axis){
    Serial.print(F("zincPin=")); Serial.println(a.zincPin);
    pinMode(a.zeroPin, INPUT_PULLUP);
    pinMode(a.zincPin, INPUT_PULLUP);
   }
  Serial.print(F("X zeroPin=")); Serial.println(axis[x].zeroPin);
}

void loop() {
  for (auto &a : axis) {
    if (digitalRead(a.zincPin) == LOW) {
      Serial.print(F("pressed zincPin=")); Serial.println(a.zincPin);
    }
  }
}
//
2 Likes

What hardware do you have? Normal digitalRead() returns either HIGH or LOW:

https://www.arduino.cc/reference/en/language/functions/digital-io/digitalread/

OPS...I posted the wrong code, the one I have been playing with rather than the original that works

Thanks for the tips.

I am not very good with arrays, but will give it a go and see how I go

#include <Encoder.h>  // Uses 5um (0.005mm) incremental glass scale linear encoder https://www.vevor.com.au/linear-scale-c_10122/250mm-linear-scale-for-milling-lathe-machine-drilling-3m-signal-cable-precision-p_010844850404

Encoder scale1(2, 4);  // Spread interupt pins 2 & 3 accross each scale,
Encoder scale2(3, 5);

const int ZincPin = 6;
const int ZeroPin = 7;

int ZeroPinState = 0;  // used in loop() to switch to zero() to set zero point for zero measurment when read head set, regardless of intitial power-up position of read head
int ZincPinState = 0;  // used to creat incremental value from zero when read head is set to an incremental point.

long ref1 = 0;  // reference value
long ref2 = 0;  // reference value
long incpos = 0;
long Zinc = 0;
long Zero = 0;
long oldPosition1 = 0;
long oldPosition2 = 0;
long oldPosition3 = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Zero set button:");
  pinMode(ZeroPin, INPUT_PULLUP);
  pinMode(ZincPin, INPUT_PULLUP);
}

void loop() {
  ZeroPinState = digitalRead(ZeroPin);
  if (ZeroPinState == LOW) {

    long newPosition1 = scale1.read();
    if (newPosition1 != oldPosition1)
      oldPosition1 = newPosition1;

    long newPosition2 = scale2.read();
    if (newPosition2 != oldPosition2)
      oldPosition2 = newPosition2;

    ref1 = newPosition1;
    ref2 = newPosition2;
    Serial.print(ref1 / 200.00, 3), Serial.print("mm X-Slide : "),
      Serial.print(" ---ZeroPinstate = "), Serial.println(ZeroPinState);
  } else {
    zero();
  }
}
void zero() {

  ZincPinState = digitalRead(ZincPin);
  if (ZincPinState == CHANGE) {
    long newPosition3 = scale1.read();
    (newPosition3 != oldPosition3);
    oldPosition3 = newPosition3;
    Zinc = (newPosition3 - ref1);
    Serial.print(Zinc / 200.00, 3), Serial.print("  =Zinc=  :");
    Serial.print(ref1 / 200.00, 3), Serial.print("  =ref1= : ");
    Serial.print(newPosition3 / 200.00, 3), Serial.print("  ==newpos3==  ; ");
    Serial.print(scale1.read() / 200.00, 3), Serial.print("  ==scale1==  ; ");
    Serial.print("=ZincPinstate "), Serial.print(ZincPinState), Serial.println(" ~~ void zero() ");
  }

  else {
    zinc();
  }
}

void zinc() {
  long newPosition3 = scale1.read();
  (newPosition3 != oldPosition3);
  oldPosition3 = newPosition3;
  incpos = newPosition3 - ref1;
  Zero = (Zinc + ref1) - newPosition3;
  Serial.print(incpos / 200.00, 3), Serial.print("  incpos C : ");
  Serial.print(Zero / 200.00, 3), Serial.print("  Zero  C : ");
  Serial.print(" ZincPinstate = "), Serial.print(ZincPinState), Serial.println(" ~+~ void zero() ");
}
//delay(1000)

this code

    long newPosition1 = scale1.read();
    if (newPosition1 != oldPosition1)
      oldPosition1 = newPosition1;

    long newPosition2 = scale2.read();
    if (newPosition2 != oldPosition2)
      oldPosition2 = newPosition2;

    ref1 = newPosition1;
    ref2 = newPosition2;

is basically this

ref1 = oldPosition1 = scale1.read();
ref2 = oldPosition2 = scale2.read();

is this what you want to do?

1 Like

Basically yes....

The ref2 came about when trying to figure out setting the "inc" zero after the "zero" function. ref2 is basically not needed for a single encoder. But would now be used for scale2.

I can read both scales at the same time, just not perform functionality I need setting "Zero" & "Inc"

can you clarify exactly what zinc and zero mean (in plain English)?

you still have this

what hardware do you use ? digitalRead does not return CHANGE.

Zero is the zero position when you set the reader at once you work out where it needs to be. The Zero can be anywhere on the scale as all work pieces and tools have different measurement

Zinc is an incremental Zero point taken from Zero.

I am using it on a lathe, so you would set the tool position, then Zero that position. Then if you need to make a cut, say 10mm away from Zero, you move the tool 10mm and use Zinc to zero that new position. you could then move the tool to another point, set a new Zinc and still have your original Zero position. You use incremental measurements a lot if you are doing something like a series of grooves or cuts in a piece or doinf things like axles with bearing faces

Are ZincPin and ZeroPin attached to buttons or switches?

The (newPosition3 != oldPosition3); above doesn't do anything. It is unclear what you want to have happen.

From what you've shared, it sounds like you want something like a multi(2)-axis DRO, with an absolute and a incremental mode and the ability to zero in either mode.

I think your code is trying to do/should do some state-change-detection, as in https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection/ but things look confused. It looks like you might be using Zinc to switch between displaying in absolute mode or incremental mode and zeroing or something, and it really isn't clear. I think I'd think of it as an axis either being in incremental or absolute mode, and depending on the mode, the zero button would either zero the encoder with scale1.setPostion(0) scale1.write(0) or record the current position as the incremental offset.

It is far easier to tell what is going on if you act and print when stuff changes from one level to another rather than when it is at a particular level.

I'd change towards the Input-Process-Output model and move the reading of the two buttons/switches in the first part of loop, then decide what you want to do if one or both are pressed/changed, and then wrap your position reporting in change detection on the positions with something like this completely untested snippet:

void reportPos() {
  const float CPMM = 200.0;
  static long oldScale1Pos = 0, oldScale2Pos = 0;
  if (scale1Pos != oldScale1Pos || scale2Pos != oldScale2Pos) {
    oldScale1Pos = scale1Pos;
    oldScale2Pos = scale2Pos;
    if (incrementalMode) {
      Serial.print("INC:  x:");
      Serial.print((scale1Pos - scale1Offset) / CPMM, 3);
      Serial.print("mm,  y:");
      Serial.print((scale1Pos - scale2Offset) / CPMM, 3);
      Serial.print("mm");
      Serial.println();
    } else { // absolute mode
      Serial.print("ABS:  x:");
      Serial.print((scale1Pos) / CPMM, 3);
      Serial.print("mm,  y:");
      Serial.print((scale1Pos) / CPMM, 3);
      Serial.print("mm");
      Serial.println();
    }
  }
}

thanks for the explanation.

You still did not address

CHANGE is defined in Arduino.h

and you are "lucky" because it's the same value as HIGH

So what you are testing is for ZincPinState to be HIGH but it does not mean the pin changed from LOW to HIGH.

Using one of the numerous button library such as Button in easyRun or OneButton or Toggle or EasyButton or Bounce2, ...would help you perform a simple change detection and handle bouncing if any.

1 Like

Basically, yes. A very basic DRO. Once I get this little bit of code sorted I can add all the display code for a Touch TFT screen

I was also thinking that when "Zero" is pressed/set I could do a write.scale1(0) and that will set the encoder back to Zero, instead of taking whatever initial measurement and subtracting it to get Zero

Yes. You can make the encoders adjust and remember the absolute zeros like that. For the incremental mode, you need to remember a separate reference offset for each axis (scale1Offset & scale1Offset in my snippet). Try moving all the inputs to the top of loop(), and then, once you know whether Zero or Zinc is activated or not, you can tell whether you are in incrementalMode or not and whether you should write to the encoders or save the encoder offsets.

Your variable names are confusing-- Are you anticipating adding another input like Xinc and having, for example, scale1 in absolute mode while scale2 is in incremental mode? Or could you do it with using one input for one absolute vs incremental mode?

There are a lot of different ways to do this, and it really depends on clear decisions about the Inputs and the Outputs and what Processing needs to be done in the black box between the Input and the Output.

The confusing bit is that I have included scale2 in my original code, but dont use it ( apart from the section with read.scale2& newPosition2 )

Zinc = Zero increment set point scale1
Zero = Zero set point scale 1

It will basically be with changed to ( just a name but it allows me to keep track of what is what with relation to the machine it is going on) and also what will be displayed on a screen
scaleX
X-Zinc
X-Zero

scaleY
Y-Zinc
Y-Zero

I will need each scale to be able work independent, as one changes its zero & inc points, the other needs to remain unchanged so just 1 input function will not work. I will need 4 inputs, 2 per scale

Yes there are. I have tried a different methods using pin interrupts etc, but found encoder.h is absolutely perfect and very responsive, accurate & repeatable.

I am just trying to work out how to make it all work when each scale is changing modes independent

I tried to explain in #2 that when you made it working for one encoder this should also work with 2. And it should even work without duplicating the code or variables.

So if you have questions you should post your updated code and describe what's not working.

I am going into uncharted territory for me with arrays.. know nothing about them

I have tried to mash up code from Reply#2 into my code...but I cant make it make sense

Do I need to start with changing

Encoder scale1(2, 4);  --> Encoder EncA(2,4)
Encoder scale2(3, 5); --> Encoder EncB (3,5)

And how do I define the 4 "buttons" for the X & Y Zero and Zinc set points ( Note:- the "buttons " might be a phsycal button, TFT button, switch or whatever else works and is reliable )
const int ZincPin = 6;
const int ZeroPin = 7;


#include <Encoder.h> // Uses 5um (0.005mm) incremental glass scale linear encoder https://www.vevor.com.au/linear-scale-c_10122/250mm-linear-scale-for-milling-lathe-machine-drilling-3m-signal-cable-precision-p_010844850404

Encoder scale1(2, 4);  // Spread interupt pins 2 & 3 accross each scale,
Encoder scale2(3, 5);

const int ZincPin = 6;
const int ZeroPin = 7;

/* --------- From original code as a refference --------
int ZeroPinState = 0;  // used in loop() to switch to zero() to set zero point for zero measurment when read head set, regardless of intitial power-up position of read head
int ZincPinState = 0;  // used to creat incremental value from zero when read head is set to an incremental point.

long ref1 = 0;  // reference value
long ref2 = 0;  // reference value
long incpos = 0;
long Zinc = 0;
long Zero = 0;
long oldPosition1 = 0;
long oldPosition2 = 0;
long oldPosition3 = 0;
    ------END ---- */


// a structure to hold
struct Axis {
  const uint8_t zincPin;
  const uint8_t zeroPin;
  Encoder scale;
  int zeroPinState = 0; // used in loop() to switch to zero() to set zero point for zero measurment when read head set, regardless of intitial power-up position of read head
  int zincPinState = 0; // used to creat incremental value from zero when read head is set to an incremental point.
  long refone = 0;        // a bad idea to name variables 1, 2, -  either name it or consider to use an array
  long reftwo = 0;
  long zeropos = 0;
  long inc = 0;
  long ero = 0;
  long oldPositionone = 0; // a bad idea to name variables 1, 2, 3 -  either name it or consider to use an array
  long oldPositiontwo = 0;
  long oldPositionthree = 0;
  Axis (uint8_t encA, uint8_t encB, uint8_t zincPin, uint8_t zeroPin) : zincPin(zincPin), zeroPin(zeroPin), scale(encA, encB) {}
};

Axis axis[2] {
  {2, 4, 6, 7},
  {3, 5, 8, 9}
};

constexpr uint8_t x = 0;
constexpr uint8_t y = 1;

void setup() {
  Serial.begin(9600);
  for (auto &a : axis){
    Serial.print(F("zincPin=")); Serial.println(a.zincPin);
    pinMode(a.zeroPin, INPUT_PULLUP);
    pinMode(a.zincPin, INPUT_PULLUP);
   }
  Serial.print(F("X zeroPin=")); Serial.println(axis[x].zeroPin);
}

void loop() {
  for (auto &a : axis) {
    if (digitalRead(a.zincPin) == LOW) {
      Serial.print(F("pressed zincPin=")); Serial.println(a.zincPin);
      long newPositionone = scale1.read();

    if (newPositionone != oldPositionone)
      oldPositionone = newPositionone;

    long newPositiontwo = scale2.read();
    if (newPositiontwo != oldPositiontwo)
      oldPositiontwo = newPositiontwo;

    refone = newPositionone;
    reftwo = newPositiontwo;
    Serial.print(ref1 / 200.00, 3), Serial.print("mm X-Slide : "),
      Serial.print(" ---ZeroPinstate = "), Serial.println(zeroPinState);
  } else {
    zero();
  }
}
}
void zero() {

  zincPinState = digitalRead(zincPin);
  if (zincPinState == CHANGE) {
    long newPositionthree = scale1.read();
    (newPositionthree != oldPositionthree);
    oldPositionthree = newPositionthree;
    inc = (newPositionthree - refone);
    Serial.print(inc / 200.00, 3), Serial.print("  =Zinc=  :");
    Serial.print(refone / 200.00, 3), Serial.print("  =ref1= : ");
    Serial.print(newPositionthree / 200.00, 3), Serial.print("  ==newpos3==  ; ");
    Serial.print(scale1.read() / 200.00, 3), Serial.print("  ==scale1==  ; ");
    Serial.print("=ZincPinstate "), Serial.print(zincPinState), Serial.println(" ~~ void zero() ");
  }

  else {
    zinc();
  }
}

void zinc() {
  long newPositionthree = scale1.read();
  (newPositionthree != oldPositionthree);
  oldPositionthree = newPositionthree;
  incpos = newPositionthree - refone;
  ero = (inc + refone) - newPositionthree;
  Serial.print(incpos / 200.00, 3), Serial.print("  incpos C : ");
  Serial.print(ero / 200.00, 3), Serial.print("  Zero  C : ");
  Serial.print(" ZincPinstate = "), Serial.print(zincPinState), Serial.println(" ~+~ void zero() ");
}
}
}

if you want to go with arrays, you should do something like

Encoder scales[] = {{2, 4}, {3, 5}};
const byte zincPins[] = {6, 8};
const byte zeroPins[] = {7, 9};
const byte scaleCount = sizeof scales / sizeof *scales;

you could also add static asserts to ensure the right number of pins have been declared

static_assert(sizeof zincPins / sizeof *zincPins == scaleCount, "Mismatch between number of zinc pins' count and scales' count");
static_assert(sizeof zeroPins / sizeof *zeroPins == scaleCount, "Mismatch between number of zero pins' count  and scales' count");

then you write your logic within a for loop using an index

for (byte i = 0; i < scaleCount; i++) {
   // use scales[i], zeroPins[i] and zincPins[i]
} 

Next stage would be to pack everything needed for one encoder into a structure and then go to a class.

1 Like

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