How To Use A MCP23017 I2C Port Expander With The Raspberry Pi – Part 1

MCP23017 Example CircuitFor all of my projects I have used the standard GPIO header pins as inputs and outputs. This gives you a total of 17 pins to play with but what if you need more?

The easiest way of getting more inputs and outputs is to use an “i/o port expander”. This is a device that allows you to control a number of ports using data you send to the device.

Other people have has lots of success using I2C devices so I decided to give one a try. I2C is a serial communications protocol which allows chips to swap data on the same “bus”. A port expander takes the data and controls the appropriate pins. This allows lots of sensors and devices to be controlled using only a few of the Pi’s GPIO pins.

The Hardware Set-up

MCP23017 16-bit Port ExpanderThe device I chose was a MCP23017 I2C port expander with a total of 16 ports. To drive it you use the two I2C pins on the Pi’s GPIO header (Pins 3 and 5). This would give us 31 (15 + 16) inputs or outputs to play with!

It’s actually possible to drive multiple port expanders giving you a huge boost in the number of inputs or outputs available. Adding another MCP23017 would increase the GPIO count from 31 to 47 (15 + 16 + 16). All for a few £/$.

The great thing with these devices is that they are cheap (a couple of £/$) and require very few external components.

In this example I’ve got 3 LEDs and a push switch connected to the device which is being driven by the two I2C pins on the Pi. I’ve used 330 ohm resistors to limit the current through the LEDs. You can use other suitable values but I tend to use 330 (as I’ve lots of them available due to my BerryClip stock!) :

MCP23017 IO Expander ExampleThe switch resistor is 10Kohm and ties the input to ground. This insures the input is Low until the switch it pressed. Pressing the switch places 3.3V on the input resulting in it going High. The resistor stops this resulting in a short circuit between the two power rails.

  • Pin 9 (VDD) is connected to 3.3V
  • Pin 10 (VSS) is connected to Ground
  • Pin 12 (SCL) is connected to Pin 5 on the Pi GPIO
  • Pin 13 (SDA) is connected to Pin 3 on the Pi GPIO
  • Pin 18 (Reset) should be set high for normal operation so we connect this to 3.3V
  • Pins 15, 16 & 17 (A0-A2) determine the number assigned to this device. We are only using one device so we will give it a binary zero by setting all three of these pins to 0 (ground)

Here is a photo of my test circuit built on a small piece of breadboard :

MCP23017 Example Circuit

The System Set-up

To use I2C on the Pi you need to enable a few things in Raspbian as by default it is not enabled. This is a fairly easy process. First you need to edit the modules file using :

sudo nano /etc/modules

and add the following two lines :

i2c-bcm2708
i2c-dev

Use CTRL-X, then Y, then Return to save the file and exit.

Then you need to edit the modules blacklist file :

sudo nano /etc/modprobe.d/raspi-blacklist.conf

and put a # symbol at the beginning of the two lines so that they look like this :

#blacklist spi-bcm2708
#blacklist i2c-bcm2708

Use CTRL-X, then Y, then Return to save the file and exit. The last step is to install some utilities we can use to test our I2C setup :

sudo apt-get install python-smbus i2c-tools

Once this is complete you can shut down your Pi using :

sudo halt

Wait ten seconds, disconnect the power to your Pi and you are now ready to connect your circuit.

Testing The Hardware

Once you’ve connected your hardware double check the wiring. Make sure 3.3V is going to the correct pins and you’ve got not short circuits. Power up the Pi and wait for it to boot.

If you’ve got a Rev 2 Pi then type the following command :

sudo i2cdetect -y 1

If you’ve got a Rev 1 Pi then type the following command :

sudo i2cdetect -y 0

Why the difference? Between the Rev 1 and Rev 2 versions of the Pi they changed the signals that went to Pin 3 and Pin 5 on the GPIO header. This changed the device number that needs to be used with I2C from 0 to 1.

I used a Rev 1 Pi and my output looked like this :

pi@raspberrypi ~ $ sudo i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

This shows that I’ve got one device connected and its address is 0×20 (32 in decimal). This is because the three address pins are set low. If you set A0 high the address would become 0×21 (33 in decimal). How you set A0, A1 and A2 is up to you but all your I2C devices must have a unique address.

Command Line Test

To do a quick test we can use the command line to enable the LED on GPA0 :

First we configure Port A pins GPA0-6 as outputs and GPA7 as an input. (10000000 in binary and 0×80 in hex) :

i2cset -y 1 0x20 0x00 0x80 

Then we set GPA0 to logic high which will enable the LED :

i2cset -y 1 0x20 0x14 0x01

To turn off the LED we use :

i2cset -y 1 0x20 0x14 0x00

Remember to replace the 1 with a 0 if you are using a Rev 1 board.

It’s time to start using those extra GPIOs …

Check out How To Use A MCP23017 I2C Port Expander With The Raspberry Pi – Part 2 for instructions on how you can control outputs using Python scripts.

Check out How To Use A MCP23017 I2C Port Expander With The Raspberry Pi – Part 3 for instructions on how you can control inputs using Python scripts.

Here are some photos of my test circuit for reference :

This entry was posted in Hardware, Python and tagged , , , . Bookmark the permalink.


16 Responses to How To Use A MCP23017 I2C Port Expander With The Raspberry Pi – Part 1

  1. Kev Partner says:

    Great tutorial, thanks. Presumably this approach also affords some protection against short circuits etc to the gpio pins?

  2. Max says:

    This is great, for those of us with failing eyesite can you give the value of the resistors you used.

    Regards

    • Matt says:

      I’ve updated the text. They are 330 ohm. I used them because I’ve a lot of them available but you could use other values if required.

      • Max says:

        Matt,

        Thanks for the fast response, what about the other resistor between the switch and ground (what does this do?) A lot of this is new to me so forgive my questions,
        I built a chess computer using an RPI & an Arduino, (www.chess.fortheray.co.uk) I needed the Arduino because of the lack of pins on the PI, but using the port exander should enable me to do it all on the RPI.

        Thanks,

        Max

        • Matt says:

          The switch resistor is 10Kohm. I’ve updated the text to give a bit more explanation about its purpose. When the switch is not being pressed it connects the input to ground (Low). When the switch is pressed the input sees 3.3V (High). Current flows through the resistor but it is only 3.3/10 mA so nothing noticeable.

          • Max says:

            Matt,

            I have worked my way through all your excellent tutorials and I now think I have all I need for my project. I am building a wooden chess computer that senses moves using 64 reed switches, and signals moves with 64 LEDS. Even with multiplexing I need 24 inputs. see http://www.chess.fortherapy.co.uk .
            In my original project I used an Arduino to provide the inputs, communicating serially to the RPI, but now I should be able to do everything on the Pi. Thanks again, Max

  3. Rhys says:

    Hi,

    This is working great for me now. But I can’t figure out how to use the b0-7 pins? Any help?

  4. Timbergetter says:

    Many thanks Matt for this clear tutorial. It has given me the confidence to progress my internet radio project to now include many preset swithed stations. One worry is the time it will take to poll all the switched inputs, but I can only try.
    Cheers, Timbergetter.

  5. I followed your procedure above. My response to the i2cdetect command is like yours but when I try to turn on the LED connected to gp0 nothing happens (command return code “0″). My wiring diagram is in post 31 at http://dicks-raspberry-pi.blogspot.com/

  6. Sonia says:

    Matt, is there a ‘mouse-o’ on the circuit picture? On the bottom 5V rail just below the digits ’01′ of the mcp23017 there appears to be an extra horizontal connection.

    • Matt says:

      That’s deliberate. On the breadboard power rails there are gaps so I had to draw a horizontal connection to attach the wire to a hole. I don’t like diagonal lines :)

  7. Average Man says:

    Hi. I’m trying to use the MCP23017 to power a simple segment display (onme pin per segment). Problem is, the segment uses one shared 3.3V connection, and then each segment is controlled by connecting a ground.

    For the life of me I can’t work out how to switch on/off a GND connection with this chip – it seems to only support output power via each pin. Any ideas?

  8. jamie says:

    great tutorial! I’m gonna try using this to create an oven mapping device (9 temperature probes – each corner plus one centre). I need a lot of pins for resolution in the AtoD conversion. I gather activating each MCP23017 in sequence in the software code will take a sample temperature reading from each probe in turn (though there are only eight addresses available so eight MCP chips/probes?). I’m still very much in the early stages.

    • Matt says:

      The MCP23017 is a port expander so it gives you 8 inputs and outputs. These are just logic levels. It isn’t an ADC so can’t read analogue inputs. It depends on the probes but you should look at my MCP3008 posts. That is an ADC with 8 inputs. You can use two devices to read 16 analogue channels with 10-bit precision.

      • jamie says:

        Hi, Thanks for the reply. I’m really keen for as great an accuracy as i can get (probably +/- 0.25 degC at best if i’m lucky). I’ve been checking out the Maxim31855 (maybe one chip per probe) due to its 14 bit resolution and cold junction compensation. The output is a serial output, I thought I might have to use a multiplexer, but if I can avoid it and sample temperature across all nine points then so much the better?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>