Cd4051 Multiplexer Tutorial Is Here!

Daisy Seed comes with 12 ADC pins.
But what if you need more??

Introducing cd4051 multiplexer!

Multiplexer_4051

In short, this multiplexer will allow us to turn 1 ADC into 8 ADCs. Let’s learn how to use it!!

How Does It Work? :thinking:
Ok, this thing looks…a bit intimidating. There are lot of pins and they’re not labeled like in a typical breakout board.

So, let’s have a look at a diagram like this.

Still a bit daunting, but we are getting closer to understanding how it works!

Let’s have a look at pin 3, which is labeled “COM OUT/IN”. For our purpose of expanding the ADC, that’s the pin where we connect Daisy’s ADC.
So, let’s connect to ADC 0!


Green wire is Daisy’s ADC 0 pin

Next, we see those “Channels I/O” pins with a number next to each. For our purpose of expanding the ADC, think of these as the expanded ADC.

For example, we can connect a potentiometer’s analog out to pin 13, which we can think of as input 0. Then, we can connect a 2nd potentiometer to pin 14, which we can think of as input 1. And so on up to 8 total inputs!
Let’s start with 4 potentiometers for now.

But, if we have data from 8 potentiometers all at once into a single ADC, that’s not gonna end pretty, right?

Enter Selector Pins :point_up_2:
Pins 11, 10, and 9 are called “select” pins (S0, S1, and S2).

These pins will help us give order to the multiple inputs (maximum of 8 sensor data) coming into a single stream (Daisy’s ADC).
It works by sending digital outs from Daisy to these pins!!
From digital out pins, Daisy can output either a 0 (low) or 1 (high).

Let’s connect Daisy’s digital pin 0 (D0) to multiplexer’s pin 11 (S0). Then, Daisy’s pin D1 to multiplexer’s pin 10 (S1). Finally, Daisy’s D2 to multiplexer’s pin 9 (S2). Note: any pin with the label ‘D’ can be used as a Digital GPIO.

While at it, connect 3.3 volt to multiplexer’s VDD input (pin 16). And also ground pins 6, 7, and 8 together like this.

Well, look at that!! We actually finished hooking up Daisy to the multiplexer!

But, we still need to understand what these select pins are.

Let’s think of doors :door:

Again, we’ll start with 4 inputs for now.

Each multiplexer inputs have 3 different combination of “doors” or “gates”. And a door opens when a matching “key” (digital outs from Daisy’s digital pins) is given. What the heck is this analogy?

Color blue is “digital out LOW or 0” and red is “digital out HIGH or 1”. The first potentiometer on the left is connected to input 0 of the multiplexer. And the next potentiometer to the right is connected to input 1 of the multiplexer and so on…

Let’s take a look at an example.

For S0 = low, S1 = low, and S2 = low, all 3 doors for input 0 will open. For all the other inputs, at least one door is closed, so the data cannot go through; it is gated off!

For S0 = high, S1 = low, and S2 = low, all 3 doors for input 1 will be opened.

And so on…

SO! Multiplexer works by selecting which input on the multiplexer can go through one at a time. And this input is selected by sending different combination of the digital outputs to the selector pins. Let’s take a closer look with a code!

Code (Arduino IDE) :keyboard:
Here’s the whole code for 8 inputs

#include "DaisyDuino.h"

#define analogPin0 A0 //ADC 0 
#define pinS2 2 //Pin D2 
#define pinS1 1 //Pin D1
#define pinS0 0 //Pin D0

void setup()
{
  Serial.begin(9600);

  //Set pins D0, D1, and D2 as digital outs
  pinMode(pinS2, OUTPUT);
  pinMode(pinS1, OUTPUT);
  pinMode(pinS0, OUTPUT);
}

void loop()
{
  
  //Only reading input 0 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, LOW);   
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 1 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, HIGH);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 2 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, HIGH);
  digitalWrite(pinS0, LOW);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 3 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, HIGH);
  digitalWrite(pinS0, HIGH);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 4 of the multiplexer 
  digitalWrite(pinS2, HIGH);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, LOW);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 5 of the multiplexer 
  digitalWrite(pinS2, HIGH);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, HIGH);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 6 of the multiplexer 
  digitalWrite(pinS2, HIGH);
  digitalWrite(pinS1, HIGH);
  digitalWrite(pinS0, LOW);
  Serial.print(analogRead(analogPin0));
  Serial.print("  "); 

  //Only reading input 7 of the multiplexer 
  digitalWrite(pinS2, HIGH);
  digitalWrite(pinS1, HIGH);
  digitalWrite(pinS0, HIGH);
  Serial.println(analogRead(analogPin0)); 

  delay(50);

}

We can set pins D0, D1, and D3 as digital outs by using the pinMode() function like this.

  //Set pins D0, D1, and D2 as digital outs
  pinMode(pinS2, OUTPUT);
  pinMode(pinS1, OUTPUT);
  pinMode(pinS0, OUTPUT);

By setting all three digital pins to “LOW”, the analogRead() will exclusively be reading the data from multiplexer’s input 0. The data is then displayed using Serial.print().

  //Only reading input 0 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, LOW);   
  Serial.print(analogRead(analogPin0));
  Serial.print("  ");

Here’s input 0 of the multiplexer being displayed in Arduino IDE’s Serial Monitor. I should mention that the first knob on the left is connected to input 0 of the multiplexer. Then, the knob next to it on the right is connected to input 1 of the multiplexer and so on…

4051_0

Notice how twisting the second knob does not affect the displayed output?

To only read multiplexer’s input 1, set the digital pins to a combination of S2 = LOW, S1 = LOW, and S0 = HIGH.

  //Only reading input 1 of the multiplexer 
  digitalWrite(pinS2, LOW);
  digitalWrite(pinS1, LOW);
  digitalWrite(pinS0, HIGH);
  Serial.print(analogRead(analogPin0));
  Serial.print("  ");

4051_1
This time, twisting the first knob does not affect the displayed output

And I believe you get the idea for analyzing the rest of the code!
Let’s use 4 knobs all at once! Again, keep in mind that only 1 ADC is being used right now!!

4051_2

The inputs are being read one after the other almost simultaneously, which results in this expansion of ADC inputs!

Code (C++) :keyboard:
There is a multiplexer function in libDaisy that we can use in C++. Here’s a code that utilizes it!

/** Example of using a CD4051 multiplexor to expand the ADC inputs */
#include "daisy_seed.h"

/** This prevents us from having to type "daisy::" in front of a lot of things. */
using namespace daisy;

/** Global Hardware access */
DaisySeed hw;

int main(void)
{
    /** Initialize our hardware */
    hw.Init();

    /** Configure the ADC
     * 
     *  One channel configured for 8 inputs via CD4051 mux. 
     * 
     */
    AdcChannelConfig adc_cfg;
    adc_cfg.InitMux(seed::A0, 8, seed::D0, seed::D1, seed::D2); 

    /** Initialize the ADC with our configuration */
    hw.adc.Init(&adc_cfg, 1);

    /** Start the ADC conversions in the background */
    hw.adc.Start();

    /** Startup the USB Serial port */
    hw.StartLog();

    /** Infinite Loop */
    while(1)
    {
        /** Print the values via Serial every 250ms 
         *  Values will be 0 when inputs are 0V
         *  Values will be 65536 when inputs are 3v3
         */
        System::Delay(250); 
        
        hw.Print("Input 1: %d", hw.adc.GetMux(0, 0)); 
        hw.Print("  ");

        hw.Print("Input 2: %d", hw.adc.GetMux(0, 1)); 
        hw.Print("  ");

        hw.Print("Input 3: %d", hw.adc.GetMux(0, 2));  
        hw.Print("  ");

        hw.Print("Input 4: %d", hw.adc.GetMux(0, 3)); 
        hw.Print("  ");

        hw.Print("Input 5: %d", hw.adc.GetMux(0, 4));  
        hw.Print("  ");

        hw.Print("Input 6: %d", hw.adc.GetMux(0, 5)); 
        hw.Print("  ");

        hw.Print("Input 7: %d", hw.adc.GetMux(0, 6));  
        hw.Print("  ");

        hw.PrintLine("Input 8: %d", hw.adc.GetMux(0, 7));   
    }
}

We initialize using adc_cfg.InitMux().

adc_cfg.InitMux(seed::A0, 8, seed::D0, seed::D1, seed::D2); 

First argument is Daisy’s ADC pin. Second argument is how many multiplexer inputs we’re using. If you put 4, you may need to ground the third select pin. Rest of the arguments are for Daisy’s digital out pins. If you put 4 for the 2nd argument, you only need to put two pins.

Use the GetMux() function to read one multiplexer input at a time. Notice the second argument corresponding to the multiplexer input pins.

hw.Print("Input 1: %d", hw.adc.GetMux(0, 0));

Let’s see if this works! Here’s what the serial monitor should look like. I’m using Arduino IDE’s Serial Monitor and only printing out 4 potentiometer data at once.
The values will be 0 when inputs are 0V and 65536 when inputs are 3v3.

What’s Next? :seedling:
Hopefully this tutorial was helpful enough for you to get started!!

Mapping these expanded input data to synth parameters is going to be a fun next step!

Adding more multiplexer is another step that you could take!
I believe we can use the same digital pins on Daisy for newly added mutiplexers.
As for the code, this should be straightforward to do in Arduino IDE.

And for C++, you can reference here to get an idea of how to add more Mux functions.

Finally for Arduino IDE, there are bunch of multiplexer codes online, so I recommend checking them out for optimization (making the code succinct/clean)!

Have fun!!
Please feel free to share if you end up using a multiplexer for your next project!! And if you have experience using multiplexer, please let us know about any tips that’ll be helpful!! Thanks!!

Credits/references:
1
2
First photo

9 Likes

really appreciate this tutorial

1 Like

Can you perhaps also show what adding such a multiplexer to a board.json file would look like?

1 Like

I would love to do a follow up to this tutorial for pd2dsy and oopsy~ eventually for sure!

In the meantime, here are the steps for anyone curious to try it out before I can:
-Include 3 digital outputs and 1 adc in the .json file.
-In the patcher, change the combination of digital outputs in sequence while reading the analog input

1 Like

With the current json implemenations of pd2dsy, and the update staged for release on oopsy you can do the following, which is copied from the daisy field.json file:

"parents":
  "pot_mux": {
    "component": "CD4051",
    "mux_count": 8, // the number of pots connected
    "pin": {
      "adc": 16,  // the ADC input pin
      "sel0": 21, // The SEL0 digital output pin
      "sel1": 20, // The SEL1 digital output pin
      "sel2": 19  // The SEL2 digital output pin
    }
  }
},
"components": {
  "knob1": {
    "component": "CD4051AnalogControl",
    "index": 0, // which analog input pin on the CD4051 to read
    "parent": "pot_mux"
  },
  "knob2": {
    "component": "CD4051AnalogControl",
    "index": 3, // which analog input pin on the CD4051 to read
    "parent": "pot_mux"
  }
},

edit: updated to be more complete (using parents and components relationship).

3 Likes