6
\$\begingroup\$

I'm trying to build a MIDI controller and I'm looking for a clever way of using 20 potentiometers without having to have 20 analogue ports on the Arduino. So far all the functionality I need is for every potentiometer to print its current value on the serial monitor when I turn the knob. Is it possible at all?

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Do you want 20 rotational position encoders - or even linear? What is the original requirement, is potentiometer the only feasible means to meet it? \$\endgroup\$
    – greybeard
    Mar 18 at 21:35
  • 2
    \$\begingroup\$ The final goal is to have a midi controller where every knob controls one parameter, so there would be a 1-1 map. Do you reckon there are more efficient ways to achieve it than use potentiometers? \$\endgroup\$ Mar 18 at 21:58
  • \$\begingroup\$ I'd be surprised if pots didn't turn out to be the most cost effective approach - I just want to invite you to think out of that box before committing yourself to one solution - "in a different dimension" than suggesting an alternative MCU. \$\endgroup\$
    – greybeard
    Mar 18 at 22:02
  • \$\begingroup\$ @greybeard having looked at something similar (I've been designing a miniature stage lighting controller for Lego-scale LED lighting) pots certainly seem optimal \$\endgroup\$
    – Chris H
    Mar 20 at 10:54

9 Answers 9

10
\$\begingroup\$

By using an analogue multiplexer, like the 74HC4067, you can select which signal gets "routed" to the Arduino:

schematic

simulate this circuit – Schematic created using CircuitLab

It works by using four of the Arduino's digital outputs as an "address", to select which path from Ix to COMMON is selected. They you can read the value of the analogue input A0.

This will handle 16 inputs only, so you'll need another multiplexer one for the remaining 4. Actually, this will give you capacity for 32 potentiometers in total. Since the Arduino has another analogue input, you don't need to do any address manipulation, and can share D0 to D3 with both multiplexers. Something like this will do:

schematic

simulate this circuit


If you can't get multiplexers quickly enough, or you have a bunch of small MOSFETs lying around, and you wish to punish yourself for something, then you could try the approach below.

By switching on M1 and M2, you connect a group of up to 8 potentiometers to the power rails, thereby "enabling" them. You switch them on by bringing D0 low. Then you can read their analogue values from A0 to A7.

Then you enable the second group of 8 potentiometers by taking D0 high (disabling the first group), and bringing D1 low, read the values, and so on.

schematic

simulate this circuit

You must ensure you never have two groups enabled at the same time, (by having D0, D1 etc simultaneously low), but if you do, resistors R1, R2 ... R7, R8 ... R10 prevent any accidental short circuiting of the power supply rails.

You can have as many groups as you have spare digital outputs to enable them. You'll need two N-channel MOSFETs and 1 P-channel, for each group of potentiometers.


Update

User misk94555 had a brilliant idea. Power the groups of potentiometers from the Arduino's digital outputs! That's genious, but it does require that you take care to not overload the IO ports. I recommend trying to keep each IO sourcing/sinking less than 2mA, to keep their voltages as close to the power rails as possible. That means ideally the potentiometers should have a resistance of 20kΩ or more.

The circuit would be as follows:

schematic

simulate this circuit

This time, set all digital outputs D0, D1, D2 and D3 to high impedance, normally. If you want to read the first group of potentiometer positions, you set their power rails, controlled by D0 and D1, to high and low respectively, read the values and then return D0 and D1 to high impedance.

To read the second group, set D2 and D3 to high and low respectively, read the analogue values, and return D2 and D3 to high impedance.

You have as many groups as you have pairs of spare digital outputs.

I've never done this, so I can't vouch for its effectiveness, but it sure beats everything else for simplicity. All the complexity is in software.

The only concern I have is that the digital outputs will not be exactly 0V or 5V, so you may lose some precision in your readings. I think it's worth a try though.


Update 2

I have overlooked the fact that there is still interference from unpowered groups in the last two circuits. It was pointed out that their effect can be compensated for with maths in software (some application of superposition, perhaps), but I doubt it's worth the effort.

Maybe there's something that can be done with diodes, to redeem the idea. Perhaps hold all groups' positive rails high, and only one group's negative low, and use diodes to isolate inactive groups. It reminds me of keyboard scanning, but with pots.

\$\endgroup\$
6
  • \$\begingroup\$ I wonder if the the second method could be done without MOSFETs. Would it it still work if we connect the positive potentiometer pins together (within one bank) to one GPIO pin, and the negative pins together to another GPIO pin. When we make a measurement on that bank we set the first GPIO pin as output high, and the second one a output low. When we make a measurement on another bank, we float both of the GPIO pins. [pro] Doesn't require external MOSFETs. [con] Can inject noise from the microcontroller into the potentiometers. Requires two GPIO pins per bank of potentiometers. \$\endgroup\$
    – misk94555
    Mar 19 at 18:24
  • \$\begingroup\$ @misk94555 you might as well try it in a simulator - no reason not to. You will get some crosstalk through all the floating pins, which are not actually floating but form some complicated resistor network. You could also try to set some of the unused banks high and some low (or all high or all low) and have. In this case you have no crosstalk, only precision loss and a curve you have to calculate. \$\endgroup\$
    – user253751
    Mar 19 at 19:30
  • \$\begingroup\$ @misk94555 I was feeling all clever about my idea with the MOSFETs, but yours is just brilliant. Hope you don't mind, I've included this is my answer. \$\endgroup\$ Mar 20 at 1:56
  • \$\begingroup\$ And what about interactions between the in-use pots and the floating ones? For instance looking at your last schematic, I'm talking about R1 and R2 in-use outputs shorted by R7,VR8-wiper,VR9-wiper,R8 even if ports D2, D3 are three state. \$\endgroup\$
    – carloc
    Mar 20 at 6:43
  • \$\begingroup\$ some crosstalk through all the floating pins is the understatement of 2023. Picture the difference in all wipers of a floated group at say, the "lower" stop: 2 k between 2 terminals and all wipers at 10 k: 4.5 k. It does get better with same resistance for pot and "mix resistor". It should be possible to fully compensate in software. \$\endgroup\$
    – greybeard
    Mar 20 at 6:44
13
\$\begingroup\$

You could use the 74HC4067 analogue multiplexer. You need only one of the Arduino ADC-channels and 4 I/O-pins for switching between the 16 analogue channels.

The ATmega328P has 8 ADC channels, so you can use 7 + 16 ADC-channels.

You can get a module from Aliexpress for 50 cent.

Searche for "CD74HC4067 16-Kanal Analog Digital Multiplexer Breakout Board Modul Arduino" at Aliexpress or eBay.

\$\endgroup\$
3
  • \$\begingroup\$ "You need only one". No, you would need 2. OP has 20 post, and the 74HC4067 has only 16 inputs. \$\endgroup\$ Mar 18 at 21:35
  • 4
    \$\begingroup\$ The ATmega have already 8 ADC channels, he only need additional 12. If he is using one of the ADC-channels of the ATmega, then he have only 7 left, but with this one channel he can address 16 additional channels. This means that he would have 16+7=23 ADC channels with the 4067. This would a bit better for him as to use the 4051. For the 4067 you get many cheap modules. \$\endgroup\$
    – MikroPower
    Mar 18 at 22:36
  • 1
    \$\begingroup\$ If settling time / scan rate is an issue, using two separate 8:1 muxes would let you change one then read the other while it's settling. Or just use the same address lines for both in parallel, so you only have one settling-time per two reads on separate ADCs. You could interleave reads from the 6 non-muxed ADC channels to fill the settling-time gaps with either strategy. (7 channels used on each 8:1, and all 6 of the other onboard ADCs. Or 13 channels used on a 16:1 and the other 7 ADCs all used.) Or go for a lower-end microcontroller with fewer ADCs if you're muxing anyway? 6 is fine. \$\endgroup\$ Mar 19 at 20:11
4
\$\begingroup\$

Make an analog multiplexer using CD4051 8:1 multiplexer ICs.

  • With 3 GPIOs as address outputs for the ICs plus 3 A/D inputs, one from each IC.
  • Or with 6 GPIOs as address and chip enable outputs for the ICs plus 1 A/D input from all 3 ICs.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Right, but he need 20 ADC pins. So he have to use two ADC channels and two of the 8 channel chips. Or he is using simply one 74HC4067. \$\endgroup\$
    – MikroPower
    Mar 18 at 21:31
  • \$\begingroup\$ @MikroPower Two x 4051 = 16 inputs: not enough. OP needs three x 4051 = 24 inputs > 20 pots: enough. "simply one 74HC4067" that's only 16 inputs. They would need tow of them for 20 inputs. \$\endgroup\$ Mar 18 at 21:34
  • 4
    \$\begingroup\$ He only do not have enough ADC ports on his Arduino. He have already 8. He need 20. \$\endgroup\$
    – MikroPower
    Mar 18 at 22:39
3
\$\begingroup\$

You’ll need an analogue multiplexer such as a 4051 (that’s old-school CMOS but I’m sure there are more modern alternatives). You’d use a few, in this case three, IO pins to select the input that you want to feed into your analogue input and then perform a conversion. With a few multiplexers you can select from a many inputs as you like. The limiting factor is that you’ll need to wait a moment for the input to stabilise, so the scan rate is reduced if you have a lot of inputs.

\$\endgroup\$
2
\$\begingroup\$

Of course it is possible, in multiple different ways.

Use an external ADC, or a few of them, to have multiple potentiometers connected to a single ADC. Then you have ADC near the analog pots to reduce the noise prone analogue wiring and have a digital bus going between ADCs and Arduino.

No need to multiplex analogue signals in this case.

You could also add more MCUs with ADCs as a subsystem for handlind the analog inputs.

Or, you could just throw out the Arduino and buy a MCU that is more suitable to begin with. Many MCUs are much faster and have multiple ADCs with better bit depth for reading multiple analog channels simultaneously, in addition to just having enough analog input pins.

\$\endgroup\$
4
  • \$\begingroup\$ Could you recommend some alternative MCUs I can look into? \$\endgroup\$ Mar 18 at 22:01
  • \$\begingroup\$ Sorry but asking for suggestons what to buy is off topic here, so I can't. \$\endgroup\$
    – Justme
    Mar 18 at 22:13
  • 1
    \$\begingroup\$ My bad, I didn't mean to break any rules. Thank you for the advice \$\endgroup\$ Mar 18 at 22:22
  • \$\begingroup\$ @Justme It's not allowed for questions to ask "What should I buy?" but I believe it's allowed for answers to suggest products as supplementary information to the main answer - as long as you are not advertising them for money. Nothing wrong with saying "use a microcontroller with additional ADCs (such as this one)" \$\endgroup\$
    – user253751
    Mar 20 at 21:13
2
\$\begingroup\$

Analog mux chips are great, but they come with serious conditions.

Something I haven't seen mentioned (might have missed it) is that an analog mux has a very real resistance in its "ON" state, and this resistance is not even close to a constant value. Particularly, it varies as a function of the signal level going through it.

For this reason, the input impedance of the Arduino analog input(s) must be very high. In round numbers, if the input impedance is 10x the switch peak resistance, that produces a 10% error in the received voltage. If you pick a better mux chip such that the Arduino input impedance is 100x the switch resistance, this reduces the error 1% - ish. The real math depends on the value of the pot and the wiper position. It is messy but not complex.

\$\endgroup\$
1
  • \$\begingroup\$ Google suggests that the Arduino input impedance is specified as 100 mega-ohms. Quite large. However there is also capacitance that affects the settling time. I suspect for user input knobs it won't matter a huge amount as long as there is still some correlation between the knob position and the value measured. Good advice in general though. \$\endgroup\$
    – user253751
    Mar 20 at 21:12
1
\$\begingroup\$

I would suggest a simpler alternative solution, without using analog multiplexers or external ADC. You can get away with using just digital open-collector(open-drain) driver like ULN2003:

ULN2003

Or using a bunch of transistors (essentially the same thing): Transistors

In this configuration you would activate one transistor at a time, leaving only one channel connected and other pots essentially floating while you perform a measurement.

\$\endgroup\$
5
  • \$\begingroup\$ Let's assume this fine&dandy for six pots - What about the 20 from the question? Not saying you aren't on to something there. \$\endgroup\$
    – greybeard
    Mar 20 at 10:17
  • \$\begingroup\$ well with 20 pots you just use 20 transistors. Or 3x ULN2003. (assuming you have enough GPIOs) \$\endgroup\$
    – floppydisk
    Mar 20 at 10:19
  • 1
    \$\begingroup\$ (The "obvious" idea here is to use 3≤ a ≤7 analogue inputs and 10-a digital outputs for up to 21-25 pots.) \$\endgroup\$
    – greybeard
    Mar 20 at 11:59
  • \$\begingroup\$ And how does this guarantee accurate readings? There will be some differences in the collector voltages, and the range is only about two thirds, and non-linear. This may be suitable for something but likely not for MIDI controllers, even if they typically have 7 bit precision in the original protocol. Some controllers can be 14 bits but AVR has no 14-bit ADC. \$\endgroup\$
    – Justme
    Mar 20 at 21:26
  • \$\begingroup\$ @Justme In saturated mode, the resistance of a BJT is almost zero. Compared to overall 15k of resistance it won't make any difference. You're right about non-linearity and 2/3 range. That should be accounted for in software, in the same way as it is done for NTC thermistors for example. \$\endgroup\$
    – floppydisk
    Mar 21 at 7:54
1
\$\begingroup\$

Posting another answer because Simon Fitch's answer and misk94555's comments got me thinking about arranging resistors in a sort of "keyboard matrix" arrangement.

enter image description here

So basically we have 4 columns and 5 rows to give us a total of 20 pots. Resistors R21 - R24 are used as upper parts for voltage dividers and from the pots themselves only 2 pins are actually connected. High side is powered from pins PB0 - PB3. Low side is powered from PD3 - PD7. So for example, to read R12 value we would set PB0, PB1, PB3 as inputs, PB2 as output (and write 1 to it) then PD3, PD5..7 as inputs and PD4 as output (and write 0). This will form a voltage divider with R22 / R12 and we can read the value from ADC1 channel.

One downside here is that not full range of ADC is used (the voltage will go only from 0 to VCC/2 if R21..24 are the same value as pots) and the measured voltage will have a non-linear relationship with the actual pot's value. But the math to compensate for this is pretty straightforward.

Edit: This of course wouldn't work because as pointed out by @greybeard there are lots of parasitic paths, just like in keyboard matrices. So basically this whole idea is BS.

For the sake of completeness here's a proper version with diodes: (but then it isn't a 'pretty solution' anymore)

enter image description here

\$\endgroup\$
3
  • 1
    \$\begingroup\$ The fiendish thing is paths no-one wants to look at still exist: You want R12 to be "the bottom resistor". But there's a parallel one through R11, R1&R2. \$\endgroup\$
    – greybeard
    Mar 20 at 11:47
  • \$\begingroup\$ @greybeard Oh you're right! I checked but overlooked this for some reason. Silly me lol. Can be fixed with lots of diodes but then it probably isn't the simplest solution anymore. \$\endgroup\$
    – floppydisk
    Mar 20 at 11:53
  • 1
    \$\begingroup\$ You're going down the same route as many before, I guess, "fixing" a matrix arrangement by adding switches, if "passive" ones. (Guess how I know.) \$\endgroup\$
    – greybeard
    Mar 20 at 11:55
0
\$\begingroup\$

All of the promising answers are looking to multiplex/matrix the pots in some way, which is obviously the best way to reduce the part count.

Following the idea of multiplexing... is it required that the pot positions be read with an ADC port? What if, instead, you used a digital input port and measured the time required for an edge transition when charging or discharging a capacitor through the pot, driven by a digital output?

This would require some software work and you likely will want to average multiple readings to get a filtered value. There would be some initial precision issues due to the tolerances of your capacitor(s) which could be improved with a calibration option.

\$\endgroup\$
2
  • \$\begingroup\$ You describe one way to achieve ADC. I cannot see it answer How to connect more pots than there are analogue pins available beyond use an external ADC. \$\endgroup\$
    – greybeard
    Mar 20 at 17:22
  • \$\begingroup\$ Well, that's not the question that was asked ;) The question was how to do it without 20 analogue pins. \$\endgroup\$
    – spuck
    Mar 20 at 19:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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