(edit sidebar)
Intro to Physical Computing Syllabus

Research & Learning

Other Class pages

Shop Admin

ITP Help Pages
Tom's pcomp site
DanO's pcomp site


Multiplexer

Multiple Inputs to a Microcontroller Using a Multiplexer

Original draft by Tom Igoe, Nov. 2008

What do you do when you run out of inputs on your microcontroller? This is a common problem. For example, the Arduino microcontroller module has only six analog inputs, but what if you're building a drum glove, and you want one analog input for each finger, so you can measure how hard each fingertip hits the table when you tap on it? Or what if you're making a touch-sensitive surface that reacts as you move your hand across it? There are several solutions to this problem, but one of the easiest is to use an analog multiplexer. A multiplexer is a chip that has several inputs, one output, and a series of address pins that let you choose which input is connected to the output.

click on any image to see the large view

Multiplexer functional diagram
A multiplexer's functional diagram
This does not reflect the spatial arrangement of the pins

To use a multiplexer to expand your sensor range, you connect the multiplexer's output to the microcontroller's input, then connect sensors to the multiplexer's inputs. Then you control the address pins on the multiplexer using a few digital outputs from your microcontroller. The diagram to the left shows a typical multiplexer-to-microcontroller connection. The multiplexer shown is a CD4067B which has 16 inputs and one output. Although this tutorial is shown on an Arduino Mini, the multiplexer can be used with most any microcontroller.

By changing the state of the address pins, you change which of the sensors is being read by the microcontroller. For example, if all of the address pins are low, then the sensor on input 0 is connected to the output, and the microcontroller can read it. When A is high and the rest are low, the sensor on input 1 is read. When A is low and B is high, sensor 2 is read.


There's a pattern emerging here: the states of the inputs is a binary number. Here's a table that explains it:

A B C D Inhibit Selected channel
X X X X 1 None
0 0 0 0 0 0
1 0 0 0 0 1
0 1 0 0 0 2
1 1 0 0 0 3
0 0 1 0 0 4
1 0 1 0 0 5
0 1 1 0 0 6
1 1 1 0 0 7
0 0 0 1 0 8
1 0 0 1 0 9
0 1 0 1 0 10
1 1 0 1 0 11
0 0 1 1 0 12
1 0 1 1 0 13
0 1 1 1 0 14
1 1 1 1 0 15

In the example that follows, you'll connect three multiplexers to a microcontroller in order to read 48 photocells, in order to make a light-sensitive surface.


Parts you'll need:

Arduino mini
Microcontroller.
I used an Arduino mini
CD4067B
3 multiplexers, CD4067B
resistors
48 10Kilohm resistors
photocell
48 photocells
Hookup wire
Plenty of 22-AWG hookup wire
solderless breadboard
solderless breadboards


If you've never used a microcontroller before, you might want to start with a more basic tutorial.

Step 1: Connect the microcontroller on the breadboard


Arduino Mini with mini USB adaptor

The circuit shown here is the basic setup for an Arduino mini connected to a USB-to-serial converter. You can see power and ground from the USB are run to the rails of the breadboard so it's convenient for the other components on the board. The 0.1uF capacitor from the reset pin is connected to the RTS pin on the mini USB adaptor. This enables auto-reset when the serial port is opened, meaning you don't have to press the reset button every time you upload new code. If it gives you problems, you can remove it, and press reset every time.


Step 2: Connect the first multiplexer


Attach voltage and ground.
The inhibit pin connects to ground.

Add the address connections.

Add the output connection to the microcontroller's input.

Schematic view

The address pins are connected to pins 8, 9, 10, and 11. I chose these pins because it was convenient in terms of the board layout. Pins 8 and 9 on the Mini are on the same side as pins A and B on the multiplexer. You can change the pin numbers if your project needs those pins for something else. Just make sure that pins A, B, C, and D on the multiplexer are connected to four digital output pins in sequence, or the code won't work.

The multiplexer's inhibit pin breaks the connection between input and output when you connect it to 5 volts. Connect it to ground so the chip will work all the time.

The multiplexer's output is connected to the microcontroller's analog in pin 0.

Step 3: Add one sensor


Add one sensor to the multiplexer's input.

Schematic view.

The multiplexer's inputs are connected to 16 photocells in voltage divider circuits. The circuit for the photocell is just the same as it would be if you were connecting it directly to the microcontroller's analog in.

Add one input, then test with the code below.

/// the address pins will go in order from the first one:
const int firstAddressPin = 8;

void setup() {
  Serial.begin(9600);
  // set the output pins, from the first to the fourth:
  for (int pinNumber = firstAddressPin; pinNumber < firstAddressPin + 4; pinNumber++) {
    pinMode(pinNumber, OUTPUT);
    // set all the address pins low 
    //to connect input 0 to the output:
    digitalWrite(pinNumber, LOW);
  }
}

void loop() {
  // loop over all the input channels
  for (int thisChannel = 0; thisChannel < 16; thisChannel++) {
    setChannel(thisChannel);
    // read the analog input and store it in the value array: 
    int analogReading = analogRead(0);  
    // print the result:
    Serial.print(thisChannel, DEC);
    Serial.print(" = ");
    Serial.print(analogReading, DEC);
    Serial.print("\t");
  }
  // print a linefeed and carriage return
  // after all the channels are printed
  Serial.println();
}

// this method sets the address pins to pick which input channel
// is connected to the multiplexer's output:

void setChannel(int whichChannel) {
  // loop over all four bits in the channel number:
  for (int bitPosition = 0; bitPosition < 4; bitPosition++) {
    // read a bit in the channel number:
    int bitValue = bitRead(whichChannel, bitPosition);  
    // pick the appropriate address pin:
    int pinNumber = bitPosition + firstAddressPin;
    // set the address pin
    // with the bit value you read:
    digitalWrite(pinNumber, bitValue);
  }
}

When you see values printing out in the Serial Monitor that change when you cover the photocell, you know it works.

Step 4: Add fifteen more sensors

There's not a lot of space around the multiplexer, so it's often easier to connect your sensors on a second board, as shown here.


Eight inputs connected, numbers 0 through 7. It's wise to add all the resistors early on, so you don't have to work around the wires

All sixteen inputs connected.

It takes awhile to wire lots of sensors, so it's a good idea to test with just one multiplexer first, to make sure you wired it correctly and that it works. The test program above will test all sixteen pins of a multiplexer

Step 5: Add the other two multiplexers

Each multiplexer's output gets attached to a separate analog input of the microcontroller. The first is on analog 0, the second on analog 1, the third on analog 2. Although only three are shown here, you could easily add more for all your analog inputs.

The address pins of the additional multiplexers can all be connected to the same four pins on the microcontroller as the first one. You can see in the picture below that they're daisy-chained.


All three multiplexers connected. Note the daisy chaining of the address pins

Step 6: Add the rest of the sensors

The remaining sensors connect to the multiplexers in the same way as the ones on the first multiplexer. Because you've got so much wire to add, it's best to plan this in advance, and think about how you're going to organize the board. I checked the spacing on a few sample photocells first, then I removed them and added the resistors where I needed them, then added the connection wires, then finally added the photocells.


The completed circuit

Step 7: Program for all the inputs

Now that you've got the whole board wired, modify the preceding program to read all three multiplexers. All you need to do is to add a loop iterating over the whole routine for one multiplexer. The code below is a modification of the earlier program, but it uses an array to store all the sensor values:

int sensorValue[48];  // an array to store the sensor values

// the address pins will go in order from the first one:
#define firstAddressPin 8

int analogInput = 0;

void setup() {
  Serial.begin(9600);
  // set the output pins:
  for (int pinNumber = firstAddressPin; pinNumber < firstAddressPin + 4; pinNumber++) {
    pinMode(pinNumber, OUTPUT);
  }
}

void loop() {
  // iterate once for every multiplexer (called muxes for short):
  for (int mux = 0; mux < 3; mux++) {
    for (int channelNum = 0; channelNum < 16; channelNum ++) {
      // determine the four address pin values from the channelNum:
      setChannel(channelNum);

      // read the analog input and store it in the value array: 
      sensorValue[channelNum] = analogRead(analogInput+mux);
      delay(10);
      // print the values as a single tab-separated line:
      Serial.print(sensorValue[channelNum], DEC);
      Serial.print(",");
    }
  }
  // print a carriage return at the end of each read of the mux:
  Serial.println();   
}

void setChannel(int whichChannel) {
  for (int bitPosition = 0; bitPosition < 4; bitPosition++) {
    // shift value x bits to the right, and mask all but bit 0:
    int bitValue = (whichChannel >> bitPosition) & 1;
    // set the address pins:
    int pinNumber = firstAddressPin + bitPosition;
    digitalWrite(pinNumber, bitValue);
  }
}


Step 8: Graph it

It's not so interesting to see the numbers scroll by, so here's a Processing sketch that reads the serial data in and graphs the relative values of the sensors. It reads until it gets a newline character, then splits the string it got on the commas, and assigns each resulting value to a bar in a bar graph. This can be useful to help you figure out which of your sensors is working and which ones aren't.

// import the serial library:
import processing.serial.*;

Serial myPort;    // instance of the serial library
int[] sensorValues = new int[48];  // array to hold the sensor values


void setup() {
  // set the size of the window:
  size(800,600);
  // open the serial port. My Arduino shows up as the first port in the list.
  // Yours may not, so check to see that you have the right port.
  myPort = new Serial(this, Serial.list()[0], 9600); 
  // don't generate a serialEvent unless you get a linefeed in from the microcontroller:
  myPort.bufferUntil('\n');
  // clear the serial buffer:
  myPort.clear();
  // don't draw strokes around the shapes:
  noStroke();

}

void draw() {
  // nice green background:
  background(99,234,120);
  // dark green foreground:
  fill(63,144,74);
  // if there are sensor values, graph them:
  if (sensorValues != null) {
    // how many sensors? As many as are in the array:
    int sensorCount = sensorValues.length;
    // iterate over the array, draw a bar for each one:
    for (int thisSensor = 0; thisSensor < sensorCount; thisSensor++) {
      // calculate the horizontal position of the bar 
      // based on how many sensor reading you have:
      float hPos = (thisSensor* width/sensorCount);
      // calculate the height of the bar based on the sensor value:
      float sensorHeight = map(sensorValues[thisSensor], 0, 1023, 0, height);
      // calculate the starting vertitical position based on the height:
      float yPos = height - sensorHeight;
      // draw the bar:
      rect (hPos, height - sensorHeight, width/sensorCount, sensorHeight);
    }
  }
}

void serialEvent(Serial myPort) { 
  // read the serial buffer:
  String myString = myPort.readStringUntil('\n');
  // if you got any bytes other than the linefeed:
  if (myString != null) {
    myString = trim(myString);
    // split the string at the commas
    // and convert the sections into integers:
    int sensors[] = int(split(myString, ','));
    // make sure you have enough readings:
    if (sensors.length >= sensorValues.length) {
      // put the readings into the global array:
      for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
        if (sensorNum < sensorValues.length) {
          sensorValues[sensorNum] = sensors[sensorNum]; 
        }
      }
    }
  }
}

When it's working, you should get a graph something like this, except less even, unless you have lit your sensors as evenly as I have:


The output of the graphing program

That's it! You can apply this same technique with any type of sensor, analog or digital. Furthermore, you can add more multiplexers to as many available inputs as you have. Go forth and multiplex.

Thanks to Chris Cerrito, Amanda Berensohn, and Eduardo Lytton for the inspiration.

  Edit | View | History | Print | Recent Changes | Search Page last modified on March 24, 2010, at 08:15 AM