Introduction: Raspberry Pi Port Expander

I made this intractable, because the MCP23S17 is a very useful and cheap IC to control up to 512 IO connections only with the use of few IO pins.

In this intractable we use two MCP23S17 to control 32 IO pins with a Raspberry Pi B+.

The MCP23S17 is connected via SPI.

Step 1: The Hardware

All we need is:

  • Raspberry Pi
  • Breadboard
  • MCP23S17
  • Some wires (female to male)
  • LEDs and resistors , relais or buttons (whatever you want to control)
  • Voltmeter

I recognised that the MISO/MOSI pins seems to be reversed at some chips, so maybe you have to switch it if it dose not work. The ~RESET has to be + because its negated. Otherwise the chip won't work.

A0, A1 and A2 are to decode the chip address, in our case its 0b000 for the first one and 0b001 for the second one. You can keep this pattern until 0b111 of the last chip.

GPA0 to GPA7 and GPB0 to GPB7 are our new 16 IO pins at each IC.

More information about the MCP23S17 and it's pins you can get here.

Step 2: Connection Between Hard- and Software

To talk via SPI we have to setup the Raspberry to allow SPI communication. The easiest way to do so is to enable SPI via to raspi-config.

How do we talk to the MCP23S17? Via SPI, but what dose that mean.

SPI is a master-slave communication bus. Bus means, we can connect as much devices as we want, depending on the possibilities of our chips and how much chipselect our board has. With the raspberry which got 2 chipselect we can use 8 chips twice. For more information please read this article.

All in all, we need to send binary code to communicate with our devices. The first byte we have to send is always our device opcode which contains the hardware address of our device (0b0100)as well as the address of the chip we want to access (0b000 and 0b001) and a READ/WRITE bit (0b00/0b01). To make one byte out of this stuff, we have to use a bitwise or operator.

In javascript we can do this with this code:

var hard_addr = 0b01000000; // we appended 4 zeros to avoid a bit shift
var chip_addr = 0b0010;     // 0b0000 we also appended one zero to avoid a bit shift
var read_code = 0b01;       // 0b00
// A = hard_addr, B = chip_addr, C = read_code
				                       //   AAAABBBC	
var device_opcode = hard_addr | chip_addr | read_code; // 0b01000011

The second byte is the command we want the slave to do and the third byte is the value we want to write, if we want to write. If we want to read we just write 0x00 because this additional byte means another tick and time for the slave to execute and answer. There are lots of different commands you can also see in this pdf. Because the chip can handle input and output on its own, we just assign all of them as output.

For the communication, I use the mcp23s17 library for nodejs. Here is a short example to connect, read and write some data:

// load the library
MCPLib = require('mcp23s17'); // create new instance width device and chip_adress // needed default settings are set (more options will follow) mcp_1 = new MCPLib.MCP23S17( '/dev/spidev0.0', 0 ); // connect decive mcp_1.connect(); var status = 0b10000000; setInterval( function(){ if( status == 0b00000000 ){ status = 0b10000000; } else { status = 0b00000000; } mcp_1.write( 'B', status ); 'B', function( s ){ console.log( s ); }); }, 500);

Step 3: Software (better Nodejs-Library)

I am currently working on a npm modul for easy communication between raspberry and MCP23S17.

npm install mcp23s17

I hope this instructable help you to start with your MCP23S17 and sorry for my bad english :).

I know this is maybe not complete, so if you need help or if you have questions feel free to contact me or write comments.

Used sources:

  1. Datasheet (MCP23S17)
  2. rasp-config at
  3. mcp23s17-library
  4. spi-library
  5. Serial Peripheral Interface Bus. (2016, February 9). In Wikipedia, The Free Encyclopaedia