An array of encoder objects

Anyone know why the line in the for loop below doesnt work? sorry for being dense...

#include <Encoder.h>

#define numEncoders 2

Encoder myEncoder1(2,6);
Encoder myEncoder2(3,7);

Encoder *encoderObjects[numEncoders] = {&myEncoder1, &myEncoder2};

void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
  
}

long encoderPosition[numEncoders] = {-999};

void loop() {
  
  for (int i = 0; i<= numEncoders; i++) {
    long encoderNewPosition[i] = encoderObjects[i]->readAndReset();
  }

}

This defines a new local variable that exists only within the for loop. Drop the "long".

What do you mean by this ?

Thank you

Here's is another way, it omits the step of explicity having two encoder objects.

I also let the compiler count how many you define.

I fixed a typo encoderPositions.

The syntax for the call to the readAndReset() method is a bit different.

# include <Encoder.h>

Encoder encoderObjects[] = {{2, 6}, {3, 7}};
const byte numEncoders = sizeof encoderObjects / sizeof *encoderObjects;

void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
}

long encoderNewPosition[numEncoders] = {-999};

void loop() {
  
  for (int i = 0; i<= numEncoders; i++) {
    encoderNewPosition[i] = encoderObjects[i].readAndReset();
  }
}

Lastly, I did not remove "Objects" from the array name, eveyone knows they are objects, so you could just call them encoders.

I'm not good at naming things. Here I always wonder whther the name of an array of things should be plural or singular.

If you still want to refer to encoders by another name, you can give names to numbers

enum niceNames {Left = 0, Right};

and later use them where you would otherwise an index, like

encoderNewPosition[Left] = // whatever

a7

perfect, thank you for your help. Ultimately I'll have 6 encoders. Seemed much easier to maintain the code this way.

If you like using the arrow operator, C/C++'s intimate relationship between pointer and arrays can be exploite, viz:

    long encoderNewPosition[i] = (encoderObjects + i) -> readAndReset();

It seems that fashion is to use the object and the dot operator, not an address and arrow.

I tested the code I posted just now. Compiling doesn't always mean it functions correctly and I was not 100 percent sure I had it right. TBH I am never 100 percent sure about anything. :expressionless:

a7

When I find I need to have multiple arrays of the same size because the same index will hold related stuff, I tend to create a class to gather them together

For example if you want an Encoder object, keep the last position before reset and have a name (ie 3 attributes for an object), then that could look like this:

#include <Encoder.h>

class EncoderWithMemory : public Encoder {
  public:
    EncoderWithMemory(const char * name, uint8_t pin1, uint8_t pin2) : Encoder(pin1, pin2), name(name), lastPosition(0) {}
    long readAndResetWithMemory() {return lastPosition = readAndReset();}
    long getLastPosition() const  {return lastPosition;}
    const char * getName() const  {return name;}

  private:
    const char * name;
    long lastPosition;
};

// Create encoder objects using the subclass
EncoderWithMemory encoders[] = {{"Right", 2, 6}, {"Left", 3, 7}};

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

void loop() {
  // check each encoder to see if it has moved since we last checked
  for (auto& ewm : encoders) {
    if (ewm.read() != 0) { 
      Serial.print("Encoder ");
      Serial.print(ewm.getName());
      Serial.print(" : ");
      Serial.println(ewm.readAndResetWithMemory());
    }
  }
}
1 Like

If you are running out of pins for your encoder array you can connect multiple of them to a PCF8574 or PCF8575 (or other) port expander.

See - GitHub - RobTillaart/rotaryDecoder: Arduino library for a PCF8574 based rotary decoder - supports 4 rotary encoders. and related libraries.

Note that as you are using I2C it will be not as fast as gpio pins, however often fast enough.

Nice. This is the single most important line in the sketch you posted:

class EncoderWithMemory : public Encoder {

which when understood is to open doors unimaginable.

The name you give it is good for printing and stuff, but I would still use an enum when there is enough need in terms of referring to individual objects by, um, name to warrant doing.

FWIW, I would present this without using an automatic for loop. I'm sure that auto is just the thing in some circumstances, but it refuses to go into my coding vocabulary - I think it is too much of a step away from code that says what it does and does what it says. Perhaps we could call it simply too sweet, as much as I like syntactic sugar or whatever you might call this language feature.

My opinion from down here programming in C near the metal. :expressionless:

a7

It ain't sugar when you need it. Good luck trying to use many of the functions in the C++ library without auto. Say you have a vector of uint16_t:

std::vector<uint16_t> myVector;

And you want to search to see if it contains the value x. Would you rather let the compiler figure out the return type of std::find():

auto index1 = std::find(myVector.begin(), myVector.end(), x);

Or would you rather try to come up with the iterator type returned on your own? It's something like:

__normal_iterator<std::_Vector_base<uint16_t,allocator<uint16_t>>::pointer,vector<uint16_t,allocator<uint16_t>>>

Another would be the type of a lambda expression, which only the compiler knows:

auto funct = [x](uint8_t var) -> bool {return x == var;};

This one would not be hard to get rid of auto as the type for ewm is trivially deducted from the array

for (EncoderWithMemory& ewm : encoders) {

Thank you for the kind words. I haven't the least intention of using anything from the C++ library, if by that you refer to the giant bag of stuff attached to C++ and more or less considered a inseparable part of the language as used on larger machines.

There are some things in C++ that are worth knowing and using, and being able to read it for dealing with Arduino libraries is good.

At some point on the C++ curve it becomes less enjoyable, a point not too far from a similar one I see many who would call themselves C++ programmers also losing traction.

I am not a programmer, but if I was and was forced to say in what language I work, it would be C, something late 20th century by way of version.

a7

sure that's still a possibility — the Name was indeed for printing.

Well, it certainly is massive and admittedly, I only use a very small number of its features. But it is nice to have a professionally-written, efficient, and thoroughly-tested library implementing things like vectors, queues, maps, linked lists, sets, etc that automatically adapt to hold just about any datatype or object. On top of that, there’s an entire library of algorithms for searching, sorting, copying, iterating, etc. Again, these functions handle just about any datatype you care to throw at it. I spent countless hours in college writing code for such structures and algorithms in C. I’m now happy to use these canned C++ solutions.

1 Like

Yes those are often under appreciated / unknown by C developers

We also need to appreciate that on small microcontrollers it’s often key to understand how memory is used, if you have dynamic allocation etc. So sometimes it’s better to rely on your own code if this is an important feature.

True, but many times the structures are global and of a known size at compile time meaning you don't have to worry about the dreaded "memory fragmentation". You can also pre-allocate the memory first thing at run time. Your coding experience and style may also come into play. And of course, processors with more and more resources are constantly entering the Arduino ecosystem.

I wasn't going to take time to say, but for me the UNO is several orders of magnitude (you choose the radix) more powerful than the toys I played with in the 20th century.

So I'll keep in mind for a day that might never come when I outgrow it the things real C++ on larger machines can provide.

a7

true - but it's not just the fragmentation. for a simple (may be far fetched) example do you know how much dynamic memory is used by all the sorting algorithms or if they use recursion ➜ size of stack then matters (and is dependent on your container's content) ?

All the sorting algorithms I had to write in school required in-situ sorting (and perhaps a small side buffer). Don't remember all of them ... Quick Sort, Heap Sort, Bubble Sort, etc. Some did use recursion. They were worthwhile exercises in turning algorithms into working code, but wouldn't want to go through that again.

Anyway, point taken. Enough hijacking the OP's thread.

1 Like