Have an upcoming project where user input will be based on spinning a large cog and I have several UNOs available

Since rotary encoders need two pins, only a Mega would be able to handle the 7-8 encoders needed. Unless theres a better method of reading them?

Is it worth using an ATTiny85 per encoder and passing all this data to a mega via 1 pin each? This way, I could extend the project beyond 7-8 later?

Or is this idea out the window, because there's a much simpler way?

  • 1
    An Arduino Mega has 54 input pins, which would yield a whopping 27 encoders? Also, reading rotary encoders can be done using interrupts, which is practically zero-overhead (no polling, CPU is only shortly interrupted when an action occursi i.e. CLK has a rising edge). My point is: 1. A Arduino Mega has enough pins, 2. by using an interrupt-capable I2C GPIO expander (e.g. 16-bit MCP23017) you could also do this more efficiently. Sure the question is right? – Maximilian Gerhardt May 20 '18 at 21:02
  • I meant UNO when writing Mega, thinking maybe a mega. A fantastic should out for the MCP23017 - I will update the question to reflect – Andrew Lazarus May 20 '18 at 21:47
  • rotary encoders need two pins ... not all ... what kind do you have? – jsotola May 20 '18 at 22:19
  • I have a rotary encoder and a MCP23017 here, I'll build a proof-of-concept and come back with an answer later this day. – Maximilian Gerhardt May 21 '18 at 9:08
  • See hackaday.com/2018/04/15/rotary-encoders-become-i2c-devices . This person used one chip per rotary encoder. The position would then be retrieved by the Arduino using I2C. So using only 2 pins you could connect up to 127 encoders. Another advantage is that the device can do the debouncing, so your main Arduino does have to deal with this. – Gerben May 21 '18 at 9:15

Library and schematics are at https://github.com/maxgerhardt/rotary-encoder-over-mcp23017.

To read a rotary encoder (i.e., detect state changes of the knob), there are two possible implementations:

  • poll the state of the two output pins and check for changes
  • set up an interrupt which will be called when an output pin changes, then check for changes

Using interrupts is more efficient than always polling the pins, because an action is only executed when an actual change occurs.

Per rotary encoder you would need two pins on your microcontroller. However, we can use GPIO expanders over e.g. I2C or SPI, such as

Using a port expander over I2C has the advantage being a bus that only uses 2 pins. This means that there can be multiple (up to 127) devices on the I2C bus, and each I2C device can then e.g. provide 16 GPIO pins.

Using an ATTiny85 per encoder and possibly joining them via an I2C bus is also possible, however more expensive and more work.

I've chosen a MCP23017 here because it is interrupt-capable, has 16-bit and I had it lying around. A PCF8574 would have also worked just fine, since it has the same capabilites.

Tests were made using an MCP23017 and a KY-040.

The written library uses a modified Adafruit MCP23017 library and the Rotary library. The Rotary library also handles debouncing by using a state-machine based approach for decoding. The library is a simple header file which defines a RotaryEncOverMCP C++ object, with which a rotary encoder connected to an MCP23017 device can be easily described and managed. It has an example for the polling method and the interrupt method.

For 1 encoder, one can write down schematic like: (interrupt based)


enter image description here

Since one MCP23017 has 16 ports, we can connect up to 8 rotary encoders to one such device.

enter image description here

However the MCP23017 also has three address pins with which its I2C address can be selected. This means we can have 2^3 = 8 such device on the same I2C bus (i.e., connected to the same SDA and SCL lines).

Thus, with one I2C bus, which uses 2 pins, we can have a maximum of 8xMCP23017 and 64 rotary encoders (assuming no interrupts are used, which would need 1 pin for any number of rotary encoders). At the cost of 2 more pins on the microcontroller, we can create another software bit-banged I2C bus, each of which adds again a maximum of 64 rotary encoders.

Assuming D2 to D13 and A0 to A5 are free, on these 18 pins we can have 9 I2C buses with a total of 72 MCP23017 devices hosting a grand total of 576 rotary encoders. Whatever one might need that for.

I've tested this library with one rotary encoder and a MCP23017, but the code is structured in a way that adding more encoders is just adding an entry in an array.

enter image description here

enter image description here

| improve this answer | |
  • 1
    Fantastic. This is exactly the information I was looking for – I've ordered a handful of MCP23017 to get started. I really appreciate the depth you've gone to here – Andrew Lazarus May 22 '18 at 21:00
  • Started working on this today and i've got a setup that is exactly to the specs of the schematic, but nothing is reading on the console – did you experience a similar problem when writing the answer? and if so, what part should I be checking for – Andrew Lazarus Aug 13 '18 at 20:52
  • 1
    @AndrewLazarus run the I2C scanner scetch first to make sure you can reach the MCP23017. If not, check that the I2C lines, power and RST are connected properly – Maximilian Gerhardt Aug 14 '18 at 9:00
  • Thank you! This was good information! – mlom Sep 25 at 18:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.