[Logo] TCC discussion forum
  [Search] Search   [Recent Topics] Recent Topics   [Hottest Topics] Hottest Topics   [Top Downloads] Top Downloads   [Groups] Back to home page 
[Register] Register /  [Login] Login 


This forum will be made read only soon and new users cannot register, please ask all new questions either using GitHub discussions, or in Arduino forum tagging @davetcc.

Using 10 or more rotary encoders RSS feed
Forum Index » IoAbstraction & TaskManagerIO
Author Message
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
I have just discovered this library and I wonder whether it could help me to deploy (among other) 10-12 rotary encoders. My original plan was to use native pins on an ATmega2560, but I see that using interrupt would be probably better than to rely on polling and there are not that many interrupts available on ATmega2560 (possibly I could also use SAMD21, if it provides enough PINs for all the other controls). I planned to use MCP23017 for some 24 additional buttons, but now I am considering the possibility to use an MCP23017 for the encoders as well. However, the main README of the library mentions that all encoders should/must(?) be connected to the same "device" - I guess it means to the same MCP23017? But it is not clear whether it means that both A and B pins of all the encoders need to be connected to the same device, or just their A pins (somewhere you mention that interrupt is only needed for A pins). Anyway, a MCP23017 has "only" 16 pins, which would mean "only" 8 encoders if both A and B pins of all encoders need to be attached to the same MCP. (I suppose, at least their buttons could be attached to another one?)
Also, the MCP23017 has two INT outputs for each group of 8 pins, but I know they can be configured into one single for all 16 and I suppose that such configuration should be used here.

So, what is the best way to wire 10-12 encoders to a single controller if I want to deploy interrupts? Alternatively, I could probably also stay with the polling method, since the controller probably won't have much more to do than just polling all the encoders, buttons and a few potentiometers.

Thanks for any advice.
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
The best way to do this is to use a multi-io abstraction and connect to it as many MCP23017's as needed, see: https://www.thecoderscorner.com/products/arduino-libraries/io-abstraction/arduino-pins-and-io-expanders-same-time/

You can then use (I think) the open drain mode the interrupt pin of the device and chain many devices interrupt pins together. That way you could have up to 8 devices on the bus.

Switches will grow its internal state storage automatically, so there is no need to adjust the number of switches.

However, we get few requests for more than 4 encoders, so to use 10 or 12, you'll need to adjust the array size by defining MAX_ROTARY_ENCODERS to the size you need. If you are using regular Arduino IDE instead of a full build system, you'll need to alter SwitchInput.h in the library.

#ifndef MAX_ROTARY_ENCODERS
#define MAX_ROTARY_ENCODERS 4
#endif // MAX_ROTARY_ENCODERS
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
Thank you. I have seen that article, but now I hopefully understand it better.

By "open drain mode" you mean I can just physically connect all the INT outputs from all the MCPs together (without any additional curcuit?) and connect to a single interrupt input of the MCU? Is it safe? It seems they can also be configured to use individual interrupt inputs on the MCU when they are added to the multi-io abstraction - so the statement that it is necessary to have all encoders connected to a single physical expander/device/interrupt is not valid anymore?

Then I would probably connect always the pair of A and B pins of every encoder to one of the MCPs. And possibly also the encoder buttons? If all three signals were connected to the same MCP, that would be 5 encoders using 15 of the 16 MCP inputs. So, even two would not be enough for 11 or 12 encoders. But if I understand the idea correctly, I could also combine two MCPs with a few additional native input pins of the MCU to avoid wasting another MCP for just one encoder. Or, I suppose I could also use the rest of the MCP inputs for the additional buttons not related to the encoders?

I need to study the library deeper, I have just had a very quick look so that I did not understand everything well yet. But I should have a look whether I could just use the SAMD21 only instead of the ATmega2560, since otherwise I would have to use both anyway.
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
so the statement that it is necessary to have all encoders connected to a single physical expander/device/interrupt is not valid anymore?

There can only be one ioexpander associated with switches, but it can be a multi io allowing several devices and even Arduino pins at the same time.

In terms of connecting more than one Int pin together I would double check the data sheet as I’ve never physically tried it. From a library perspective it should be fine.
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
But if I understand the idea correctly, I could also combine two MCPs with a few additional native input pins of the MCU

If you’re using a 2560 you would have limited interrupt pins. However, you could run switches in non interrupt mode, and just put all the encoder buttons on Arduino pins while connecting all the encoder A and B pins to the expanders, as at least Pin A must be interrupt capable . That way I think two devices would suffice.
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
davetcc wrote:
so the statement that it is necessary to have all encoders connected to a single physical expander/device/interrupt is not valid anymore?

There can only be one ioexpander associated with switches, but it can be a multi io allowing several devices and even Arduino pins at the same time.


So, the limitation concerns one LOGICAL expander, which can also connect together several PHYSICAL expanders, if I understand it correctly. That is good.

And it only concerns the encoders? Well, I think I do not care that much about switches, as they are not that much timing critical as the encoders. I also have LEDs in all the switches that I want to control independently, but still have each connected to the same physical MCP module as the switch. I need to have a look whether this library can also help with that. Altogether, that is: 24 switches + 24 LEDs = 3xMCP23017, and up to 12 encoders = 36 inputs (A+B+switch from each encoder). That would be 5 MCPs + some extra pins, i.e. at least 7 different physical interrupt sources. And analog potentiometers...

I could probably also use the SAMD21 if I used an additional analog expander for the potentiometers. I found that a single PCF8575 could be enough. It does not seem to be supported by this library (just 8574, which is very similar, though!), but I have not looked whether the library also supports analog inputs. One advantage of the SAMD21 is the fact that nearly all pins can be used as interrupts. On the other hand, they are not as many as on the 2560, so I would need to double check that it can fit all the functions, including displays etc.
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
And it only concerns the encoders? Well, I think I do not care that much about switches


all encoders and switches are on the same IoAbstraction. So the multi Io would be for all switches and encoders. It doesn't matter because if you use multiIo, the first 100 pins by default are for Arduino. Then each expander in turn.

The library does allow for analog joysticks configured to map a range of values like a rotary encoder, if that is what you wanted. It also has an analog abstraction that provides a few helper functions too. I think you'd be better trying a few of the examples and seeing if the provided support does what you need.

Thanks,
Dave.
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
Yes, I noticed there is support for LEDs as well. And also for analog input, but it does not mention whether that is also supported by the expanders (At least PCF8584 has analog/ADC inputs/outputs.). But definitely nothing about 8575, it seems.
And by "switches" you probably meant the library object, not the physical buttons... another misunderstanding of myself.

I also noticed sharing of interrupt lines seems to be supported by some INT modes by the library. It just activates appropriate pull-up resistors. Good.

This task oriented library and all the others from this "toolkit" (or whatever) look very promising. Much better approach than the classical primitive Arduino-like programming. It seems definitely worth trying.

Thanks for suggestions and happy new year!
Pavel
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
Happy new year to you as well!

In terms of the PCF8575, it looks so similar to the PCF8574 that I can probably make the existing class handle both cases with some configuration flag, as it's just essentially like programming the existing device with two ports. I'll see if I can pick one up at some point and add it.

At the moment we don't support I2C or SPI based analog devices, we have a plan to do so, but we are not there yet. For the analog joystick option (AnalogSwitchInput.h) you just create an analog abstraction as in the example that's packaged with IoAbstraction. The main benefit of this abstraction is that for most of the hardware we support, we've standardized the range of values using floating points between 0 and 1.
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
First test on SAMD21 using a single encoder on an MCP23017 successful, good! However, I had to look into the sources, since the documentation is rather confusing in a lot of details - a mix of obsolete and more up-to-date information. The methods automatically set up the PINs as INPUTs for me - that is nice! The most confusing are the (probably legacy) static function calls such as "setupRotaryEncoderWithInterrupt" which can be just replaced with "switches.setEncoder(0, ...)" - that seems more systematic if you use multiple encoders. I also wonder why the getEncoder() method does not have any parameter, it just returns encoder[0] - adding a parameter with default value 0 would keep it backwards compatible and make it actually useful.

Yes, I thought PCF8575 will be very similar to 8574. Without the support for analog readings it has no point for me, but others might appreciate it already now if they want to use it for digital I/O. Meanwhile, I can use other library. But I like the general approach of IoAbstraction - finally a step beyond the usual Arduino beginners paradigm and yet nothing overly complex either!
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
Apologies I had missed this reply.

However, I had to look into the sources, since the documentation is rather confusing in a lot of details - a mix of obsolete and more up-to-date information.


We are trying to clean up the documentation as best that we can, I'll take another look at the section on creating rotary encoders if it's still confusing, but you've got to understand that some of the legacy features such as the old setup methods cannot be removed as they are heavily used in 1000s of projects.

In terms of the getEncoder() method, you're right it should take an int and have a default encoder of 0. We'll try and address that in a patch, please feel free to raise an issue on the IoAbstraction repo for that. The multiple encoder support was kindly contributed some time back, so there are some areas where things are still a little rough around the edges.

davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
Without the support for analog readings it has no point for me, but others might appreciate it already now if they want to use it for digital I/O. Meanwhile, I can use other library.


I'm not really aware of any combined serial IO device that combines both Analog and Digital IO on the same I2C/SPI chip, there could well be devices I'm not aware of (because in this game you're only really aware of what you've used / may need). In terms of I2C and SPI ADC, Potentiometer, and DAC devices, we need that support ourselves for a few projects we've got lined up, but we just don't have the bandwidth to implement them at the moment. You can see our priorities in the road map section on the main site under the Products menu.

IoAbstraction coexists with most other libraries quite well, so you could use it for the encoders and switches, and use another library for the serial offboard analog devices.
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
Almost two years later I finally got to putting the project physically together and while it mostly seems to work, I am hitting some serious issues:

- the last two (of five) MCUs (23017) serve 10 of the 12 rotary encoders and their buttons - and whatever happens at one of these two (be it button press or encoder rotation), also triggers an equivalent action seemingly coming from the other one; could this be a software bug or wiring problem or problem of one of the MCUs?

- some of the encoders work quite well and some rather (more or less) erratically: not reacting on slow rotation or reacting too much on faster rotation, or just increasing instead of decreasing the counter, or even the counter suddenly falling back to a lower value before increasing again; with the exception of the last issue I would suspect the problem to be just poor quality of the cheap Chinese encoders from AliExpress, considering that some work better and some worse; but may that also be problem in software or wiring? (the encoder modules have some resistors integrated - probably pull-ups? Or interrupts? But it also applies to the two of the 12 encoders attached directly to native PINs of the microcontroller (strangely enough, these two seem to have work worst of all).

The whole set uses five MCP23017s connected to a single I2C bus of a SAMD21, configured to use addresses 0x21 to 0x25; each of them using their own INT output wired to one digital input of the SAMD21. So, the last two triggering all actions in parallel are those at 0x24 and 0x25. The first three MCUs serve other 24 push buttons with LEDs and seem to work correctly. There is another PCF8575 at the same bus at 0x20 that measures analog pots - I have not tested that one yet, since IoAbstraction still does not support analog readings via expanders, so that I will need to utilise another library for this purpose.
davetcc


Joined: Jan 19, 2019
Messages: 685
Offline
I have to admit that I have not got a test bed with more than two rotary encoders at once. But with those two encoders on a PCF8574 I2C device, I don't see any problems with crosstalk between them. In terms of encoder quality, I test with cheap encoders generally without seeing many problems.

So some point here that may help:

Rotary encoder support is very sensitive to latency on taskManager, do you have any long-running tasks, if so, this may be the cause of the problems, and you may need to see if there's a way to reduce the latency of those tasks. But overall the behaviour you're seeing does not sound like latency, it sounds like either a hardware or software problem.

I would start with one of the rotary encoder test sketches shipped with IoAbstratction, start with it configured to pick up one of the encoders, see if it works without problems, even when turning others. Then add one more and so on. Even though the library can work with many encoders, I think most have probably not gone past 2 or 3 encoders.

By default MAX_ROTARY_ENCODERS is set 4, I'm assuming you've set this to a larger value already.

Unfortunately, this is a bit outside of tested use cases, and therefore may need a bit of debugging on your side.
wanthalf


Joined: Dec 29, 2020
Messages: 7
Offline
OK, so back to deeper debugging. (I have already fixed a good deal of wrongly assigned pin numbers and loose or badly soldered wires in this wire-monster.)

The loop currently only contains the call to taskManager.runLoop(), nothing else. Listeners only send a quick message to USBSerial. Nothing more. If there is any mistake, it must be in the setup or wiring (if not HW or the library). The setup starts with:

multiIoAddExpander(multiIo, ioFrom23017(EXP1_ADDR, ACTIVE_LOW_OPEN, EXP1_INT), EXP_PIN_ALLOC);
multiIoAddExpander(multiIo, ioFrom23017(EXP2_ADDR, ACTIVE_LOW_OPEN, EXP2_INT), EXP_PIN_ALLOC);
multiIoAddExpander(multiIo, ioFrom23017(EXP3_ADDR, ACTIVE_LOW_OPEN, EXP3_INT), EXP_PIN_ALLOC);
multiIoAddExpander(multiIo, ioFrom23017(EXP4_ADDR, ACTIVE_LOW_OPEN, EXP4_INT), EXP_PIN_ALLOC);
multiIoAddExpander(multiIo, ioFrom23017(EXP5_ADDR, ACTIVE_LOW_OPEN, EXP5_INT), EXP_PIN_ALLOC);
switches.initialiseInterrupt(multiIo);


Then there is "switches.addSwitchListener(ENC01_S, &buttonListener, repeatInterval, true);" for each of the 12 encoder buttons and the other 24 buttons.
Then there is "switches.setEncoder(0, new HardwareRotaryEncoder(ENC01_A, ENC01_B, onEnc01Change, ENC_HW_ACCEL, ENC_TYPE));" for every encoder. 10 of them at the expander pins (>100), 2 of them on native SAMD21 pins (<100).

BTW, when I tried to allocate 100 pin numbers (here as EXP_PIN_ALLOC constant) for each expander, the sketch freezed and I was unable to even upload a new, fixed sketch. I had to try about 20 times before I somehow managed to start the upload just between the reboot and the freezing of the sketch. So, there is probably some limit on the amount of pin numbers on the multiIo, which bricks the whole controller? With EXP_PIN_ALLOC set to 20 it works.

Following is "switches.changeEncoderPrecision(0, maximumEncoderValue, 0, false, 1);" for each encoder.
(BTW, this was a cause of serious issues for some time: without the last two values (which should be defaults) it is actually the shorter/non-overloaded version of the method that gets called insted of the longer one and it has a completely different meaning - setting the default encoder (no. 0) to the first value as maxValue! This ambiguity is indeed very confusing!)

That is basically everything, except of initialization of all LEDs and OLED displays just for testing. (Also a strange issue, but not concerning IoAbstraction: the pin A4 does not work as CS output for the SPI displays, while A3, A5 and other pins seem to work. Maybe, there is some HW issue with the SAMD2 module itself?)

P.S.: And sorry for the confusion concerning PCF8575... I am still completely puzzled where did I get the idea that it can read analog input... and how did I manage not to notice it is a nonsense until now. Now I am looking for another solution.
 
Forum Index » IoAbstraction & TaskManagerIO
Go to:   
Mobile view
Powered by JForum 2.7.0 © 2020 JForum Team • Maintained by Andowson Chang and Ulf Dittmer

This site uses cookies to analyse traffic, serve ads by Google AdSense (non-personalized in EEA/UK), and to record consent. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Our privacy policy applies to all pages on our site

Should you need further guidance on how to proceed: External link for information about cookie management.