Position agnostic modules in a chain

I’m building a module with potentiometers and a multiplexer, which I want to have more than once, chained. All modules should send their multiplexed signal to Arduino’s analog pin 0, so that the user doesn’t have to set the position of each module in the chain manually (e.g. via a multi-position switch), 1st, because I would like to make this project as plug-and-play as possible, 2nd so that a setup of six chained modules (as many as the analog pins of the Arduino UNO are) is the maximum one can have.

For now I’m trying to achieve this with two modules only, using transistors to control which module sends its multiplexed signal to the Arduino, but it doesn’t really work. The idea is that one transistor (PNP) will work as a switch to let the multiplexed signal of a module pass or not. The signal that controls this transistor passes through another transistor (NPN), and controls the respective PNP transistor of the 2nd module. The NPN transistor has its own dedicated control Arduino pin. I have attached the schematic of the circuit as a .pdf file
For now I’m using only one potentiometer per module, and the second module seems to be sending kind of correct data, but the first module affects the second when I’m spinning its potentiometer.

And here’s the code I’m using:

#define NUM_CHANNELS 16
// define a log base 2 function
#define LOG2(x) (log(x) / log(2))
#define NUM_OF_CHIPS 2
#define NUM_POTS_PER_MUX 1
#define DELTIME 10
#define BAUDRATE 57600

int pnpTransistorPin = 6;
int npnTransistorPin = 7;

const int num_ctl_pins = LOG2(NUM_CHANNELS);

int ctl_values[num_ctl_pins];
int ctl_pins[num_ctl_pins];

// break up the analog values into two, see bottom of sketch
const int transferData = ((NUM_POTS_PER_MUX * 2) * NUM_OF_CHIPS) + 1;
// create an array to transfer all bytes to Pure Data with its first element set to 192
// this value will denote the beginning of the data stream
byte transferArray[transferData] = { 0xc0 };

void setup() {
  // set multiplexers control pins and values
  for(int i = 0; i < num_ctl_pins; i++){
    ctl_values[i] = pow(2, (i + 1)) - 1;
    // multiplexers have their control pins wired to digital pins 2 to 5
    ctl_pins[i] = (num_ctl_pins - i) + 1;
    pinMode(ctl_pins[i], OUTPUT);
  }

  pinMode(pnpTransistorPin, OUTPUT);
  pinMode(npnTransistorPin, OUTPUT);
  // open the NPN transistor and close the PNP
  digitalWrite(npnTransistorPin, HIGH);
  // short delay before we close the PNP transistors
  delayMicroseconds(DELTIME);
  digitalWrite(pnpTransistorPin, HIGH);
  delayMicroseconds(DELTIME);

  Serial.begin(BAUDRATE);
}

void loop() {
  int index = 1;
  int chip_index = 0;

  for(int i = 0; i < NUM_OF_CHIPS; i++){
    if(!chip_index){
      // close the NPN transistors
      digitalWrite(npnTransistorPin, LOW);
      delayMicroseconds(DELTIME);
      // and open the PNP ones, this will result in opening the first mux only
      digitalWrite(pnpTransistorPin, LOW);
      delayMicroseconds(DELTIME);
    }
    else{
      // open the NPN transistors
      digitalWrite(npnTransistorPin, HIGH);
      delayMicroseconds(DELTIME);
      // open all the PNP transistors
      digitalWrite(pnpTransistorPin, LOW);
      delayMicroseconds(DELTIME);
      // close the NPN
      digitalWrite(npnTransistorPin, LOW);
      delayMicroseconds(DELTIME);
      // and close the first PNP
      digitalWrite(pnpTransistorPin, HIGH);
      delayMicroseconds(DELTIME);
    }
    
    // this loop creates a 4bit binary number that goes through the multiplexer control pins
    for (int j = 0; j < NUM_POTS_PER_MUX; j++){
      for(int k = 0; k < num_ctl_pins; k++){
        // this line translates the decimal j to binary
        // and sets the corresponding control pin
        digitalWrite(ctl_pins[k], (j & ctl_values[k]) >> k);
      }
      int val = analogRead(0);
      // restrict analog readings to 7-bits for receiving them in the correct order
      // they will be re-assembled in Pure Data
      transferArray[index++] = val & 0x007f;
      transferArray[index++] = val >> 7;
    }
    // update the chip_index variable for every multiplexer
    chip_index++;
  }

  Serial.write(transferArray, transferData);
}

I would appreciate any idea on this as my electronics knowledge is very limited.
Thanks.

pots_module2.pdf (11.2 KB)

This circuit is unusable, as the added transistors will introduce completely unpredictable voltage variations. Stick with analog multiplexers alone.

jremington:
This circuit is unusable, as the added transistors will introduce completely unpredictable voltage variations. Stick with analog multiplexers alone.

But is there a way I could set the order of the multiplexers in software only? For this I guess I need a way to open/close the output of each multiplexer, or to enable/disable them. Still, in order for this to be done in software (without any involvement of the user in the hardware) there must be something in the circuit that can be controlled by the Arduino. Hope what I'm writing here is clear.

No, what you are trying to do is not clear. Describe the overall project.

How can the Arduino know which pot is "talking" if it is not indexed in hardware?

What does the user have to do with it?

jremington:
No, what you are trying to do is not clear. Describe the overall project.

I want to build some controller modules which the user can daisy chain (perhaps with magnets, for easy use) and then use a graphic software to set the number of modules in the chain (this software will generate Arduino code). Still, I would like to avoid using an extra switch or something similar on these modules, in order to make them as plug-and-play as possible. I won't be referring to experienced hardware users, so the easiest it's for the user, the better.

jremington:
How can the Arduino know which pot is "talking" if it is not indexed in hardware?

That's the part I don't know how to achieve, or if it's even possible. I would like to have some sort of switching which lets a multiplexer pass its signal or not, and that switch can be controlled by the Arduino. I guess this is some sort of relay right? But all I need to control is the multiplexer signal, no higher voltages, plus I don't know if relays can be controlled so fast (switching on and off several times within one loop).

The Arduino MUST know which pot is which. Either the pot is indexed in hardware or in software (e.g. ask the user to wiggle a pot knob, and ID it).

You are making this way too complicated by expecting one ADC with one channel to do all the work. If each pot had its own digitizer (an ATTiny, for example), it could send an ID along with digitized readings.

That is how many professionals do it, at a cost of less than $1 and a dozen lines of ATTiny code per pot.

Relays would be too slow and would quickly wear out.

But I still don't get what you are trying to achieve. So there will be 6 modules each with a number of pots. Will they all be identical circuits? Will they appear identical to the user, or will each module or pot be labeled differently?

Let's call the modules A to F and the pots on each module 1 to N. So the third pot on module C would be C3. Must pot C3 always perform the same function, whatever sequence the modules are connected in?

How will the modules be connected to each other and to the Arduino? How many pots per module?

This concept was discussed recently in another thread.
Each node has to have some intelligence, and two interfaces (upstream and downstream).
Nodes can be arbitrarily arranged in any sequential order, and may have different functionality.

When a node is powered on, it passes nothing, and listens on the downstream interface.

The host sends a request up the bus, and the first node identifies itself (random or MAC style address), then it is 'live', and passs the next request upstream - and so on - until there are no more nodes replying.

This takes milliseconds in real time, then your nodes have identified themselves, and known to the host controller.

When a node wants to send a message, it prefixes its address to the payload going downstream, or if the controller is sending a message up - same thing... the lower, intermediate nodes ignore the message if it's not addressed to them.

You can of course have 'broadcast' messages - like RESET, or ALL ON etc.

lastchancename:
This concept was discussed recently in another thread.
Each node has to have some intelligence, and two interfaces (upstream and downstream).
Nodes can be arbitrarily arranged in any sequential order, and may have different functionality.

When a node is powered on, it passes nothing, and listens on the downstream interface.

The host sends a request up the bus, and the first node identifies itself (random or MAC style address), then it is 'live', and passs the next request upstream - and so on - until there are no more nodes replying.

This takes milliseconds in real time, then your nodes have identified themselves, and known to the host controller.

When a node wants to send a message, it prefixes its address to the payload going downstream, or if the controller is sending a message up - same thing... the lower, intermediate nodes ignore the message if it's not addressed to them.

This sounds a lot like what I want to do. Is there an example you can share?

You could use an Arduino Mega for every node, and use Serial1 for the upstream, and Serial2 for the downstream.

There is a danger here that the whole design could get way more complicated than it needs to be, not to mention way more expensive. I am still not sure I understand what these modules are supposed to do, but I wonder if all that is required to achieve what you want could be as simple as a bit of lateral thinking and a bit of clever wiring?

For possible different approaches imagine you sit at the end of a tube (communication channel), and want to distinguish signals (lights?) sent from an unknown number of modules sticking to the other end of the tube, with further tubes in between.

alexandros301:
This sounds a lot like what I want to do. Is there an example you can share?

I haven't done this on an Arduino (or at all for about a decade!).
It sounds complicated, but it really isn't too hard to implement.

Any processor that supports two ports (I don't really like software serial), but it may work, so a '328 (UNO) might get away with this. Depending on distances between nodes, you may want RS485 or similar, but remember each node regenerates the up/down packets - so the overall run length isn't as much a problem.

Just define what you need to say between the nodes, and design a message format to suit - then implementing the wrapper/prefix handler/stripper for that is pretty straightforward.

You've stimulated me - maybe I'll dig out some Unos and work on an example - maybe Christmas lights fpr next year(!)

OK, this is what I think. You have 6 modules each having 8 pots connected to an analog multiplexer. You want to chain the 6 modules together in any order and connect the multiplexer outputs to an Arduino Uno's 6 analog inputs.

Connect them with ribbon cable. You will need 2 wires for 5V & ground. Plus 4 wires for the analog multiplexer's channel select lines (they can all share them). Let's call those S0 to S3. And finally 6 wires for the multiplexer's outputs. Let's call those M0 to M5.

If the modules are all labelled differently (let's call them A to E), and module D must perform the same function regardless of where it is placed in the chain, then simply connect module D's multiplexer output to line M3 that goes to A3 on the Uno, and so on.

If the modules are all identical, and it's the position in the chain that is important (i.e. the module in position 3rd away from the Uno must always perform the same function) then the wiring is slightly more complicated. In this scenario, the multiplexer output in each module always connects to the M0 outgoing line. The M0 line coming in from the previous module (4th from the Uno) connects to the outgoing M1 line, incoming M1 to outgoing M2 and so on up to incoming M4 connecting to outgoing M5. Incoming M5 is not connected because it is never needed with only 6 modules.

With the first method, you can have a single connector on each module, or if you want you can have incoming and outgoing connectors, and every connector is wired straight through from in to out. With the second method, you need separate incoming and outgoing connectors, because you need to do that wiring trick between in and out.

Does that make sense? Let me know if you are interested and need a diagram.

PaulRB:
OK, this is what I think. You have 6 modules each having 8 pots connected to an analog multiplexer. You want to chain the 6 modules together in any order and connect the multiplexer outputs to an Arduino Uno's 6 analog inputs.

Connect them with ribbon cable. You will need 2 wires for 5V & ground. Plus 4 wires for the analog multiplexer's channel select lines (they can all share them). Let's call those S0 to S3. And finally 6 wires for the multiplexer's outputs. Let's call those M0 to M5.

If the modules are all labelled differently (let's call them A to E), and module D must perform the same function regardless of where it is placed in the chain, then simply connect module D's multiplexer output to line M3 that goes to A3 on the Uno, and so on.

I've though of that, but it would ideal if I could spare these connections. What I'm trying to do is to create these modules that can be used by people who don't want to deal with any wiring (maybe it's not possible at all, in which case I'll drop the approach). I would like the user to connect the modules in a chain (ideally the modules would snap with magnets and this would replace ribbon cables and IDC connectors) and set his/her setup in a graphical software that I will be building.
The order of the modules will be set in the software, that's why I'm trying to find a way to control everything with code instead of circuitry.

PaulRB:
If the modules are all identical, and it's the position in the chain that is important (i.e. the module in position 3rd away from the Uno must always perform the same function) then the wiring is slightly more complicated. In this scenario, the multiplexer output in each module always connects to the M0 outgoing line. The M0 line coming in from the previous module (4th from the Uno) connects to the outgoing M1 line, incoming M1 to outgoing M2 and so on up to incoming M4 connecting to outgoing M5. Incoming M5 is not connected because it is never needed with only 6 modules.

I don't really get this..

PaulRB:
Does that make sense? Let me know if you are interested and need a diagram.

I am interested! A diagram would be great, thanks!

How do you make electrical connections, to the analog inputs, between the modules?

DrDiettrich:
How do you make electrical connections, to the analog inputs, between the modules?

For testing I'll use IDC connectors, but for the final product I want to make a custom pack of connectors that will connect with a couple of magnets at the sides of this pack. But I should stick to the IDC for now, until I solve all issues.

Disclaimer: very rusty knowledge of electronics

alexandros301:
For this I guess I need a way to open/close the output of each multiplexer, or to enable/disable them.

The HEF4067D has an enable input that allows to disconnect all inputs from the output; this can be used (as far as I know) to parallel outputs. Have you considered that? You currently hard-wire it to ground.

alexandros301:
Still, in order for this to be done in software (without any involvement of the user in the hardware) there must be something in the circuit that can be controlled by the Arduino. Hope what I'm writing here is clear.

One idea is to have all modules the same. As described by PaulRB, all modules will have 4 address lines, an enable line, the analog output line and power (in below 'drawing', power is omitted).

      +-----+
   ---+ A   |
   ---+ B   |
   ---+ C   |
   ---+ D   |
   ---+ E   |
   ---+ Y   |
      +-----+

Next you can design a 'backplane' where the position identifies the module.

    3 ---o---------o---------o---------o---------o---------o
    4 ---o---------o---------o---------o---------o---------o
    5 ---o---------o---------o---------o---------o---------o
    6 ---o---------o---------o---------o---------o---------o
   A0 ---o---------o---------o---------o---------o---------o

    7 ---o      +--o      +--o      +--o      +--o      +--o
                |         |         |         |         |
    8 ----------+         |         |         |         |
    9 --------------------+         |         |         |
   10 ------------------------------+         |         |
   11 ----------------------------------------+         |
   12 --------------------------------------------------+

This requires 13 pins (plus power that is omitted again).

If you make pin 7 low, you select the first module, if you make pin 8 low, you select the second module and so on.

If you add a little logic (probably two transistors and three resistors) to the enable line on each board, you will be able to detect if a board is present or not; this will require one additional Arduino pin. When you enable a board, you can check the additional line (an input with internal pull-up) to see if the board is present.

Note:
the above might be based on one of PaulRB’s descriptions, not sure.

Below diagram for a single board is based on the above and might do what you need; note that I mentioned before that my knowledge of electronics is very rusty.

module.png

@sterretje your approach seems like a good idea... still there is a hard limitation imposed by the number of digital pins on the Arduino, but again it can still take six modules which should be enough...
Thanks, I'll give it a shot!