Sometimes in your Raspberry PI or NodeMCU projects you need more inputs and/or outputs than your PI/NodeMCU is giving you. One way to do that is to add more ports using the MCP23017 (or multiple even). It is a chip that works on the I2C bus and gives you 16 new in- and outputs.
How to connect them to your Raspberry Pi or your NodeMCU, I will discuss seperately. However the general workings of the chip is the same for both, so in this post I will explain how to connect it and what the different addresses are.
I2C is a bus which means you can have multiple devices on the same bus as long as each has a different address. The address for the MCP23017 is set by connecting pins to ground or Vdd. Let’s look at the pin layout:
As you can see there are 16 in- and outputs: GPA0-7 (bank A) and GPB0-7 (bank B). These are where you connect your digital in- and outputs to. Vdd is where you connect the + to (5v or 3.3v). Vss is connected to ground. SCL and SDA are the I2C bus channels and these you connect to your Raspberry Pi or NodeMCU bus. Note that you connect SCL to SCL and SDA to SDA (as opposed to Txd and Rxd which you switch). A0, A1 and A2 is where you set your address anywhere between 0x20 and 0x27 (hex 20 and hex 27). If you connect all three to ground, your device address is 0x20. If you connect A0 to Vdd and A1 and A2 to ground, your device address will be 0x21 etc. Also connect RESET to Vdd.
Once you have connected the power, connected the I2C bus and set the address, you are good to go.
To start, you have to tell the MCP23017 whether the ports are in- or outputs. You do this by writing to addresses IODIRA (0x00) for bank A and IODIRB (0x01) for bank B. There are 8 ports per bank which correspond to 8 bits. Setting a bit high means it is an input, setting a bit low means it is an output. I assume you can convert bits to a hex number, but a quick example to help you out:
Let’s say we want the first 4 ports of bank A to be inputs and the last 4 ports of bank A to be outputs. In bits this would mean that the first 4 bits are 1 and the last 4 bits are 0. If we write this out (starting with port 7 at the left and port 0 at the right), we get 00001111 which is the value in binary. If we convert this to hexadecimal we get 0x0F (add 1 for the bit at the right, than 2, than 4 and lastly 7. This gives 15 which is F in hexidecimal).
Of couse you can do the same for bank B. Now let’s tell the MCP23017 in pseudo code (the actual code of course differs per hardware platform) that the first 4 ports of bank A are inputs and the last 4 ports of bank A are outputs. We are assuming A0, A1 and A2 are connected to ground so the device address is 0x20.
device_address = 0x20
IODIRA = 0x00
bankAInOutput = 0x0F
Next we want to write to the outputs. We do a similar exercise, except we need a different address and we need to determine which ports to set high. Let’s say that we wish to turn all our outputs high. We need to convert this to a hexadecimal number as well. The first 4 bits (starting at the right) are for input, so we leave these at 0. The last 4 bits we want to set high. In binary we want to write the value 11110000 which corresponds to 0xF0. The address we want to write to is called OLATA and has address 0x14. Assuming we have executed our pseudo code above, we just need to do this to set the 4 outputs high:
OLATA = 0x14
All4OutputsHigh = 0xF0
Next, let’s see what our input is doing. These are the first 4 bits. To read our bank we need address GPIOA which has address 0x12. When we read this address we do not only get our inputs back but also the state of our current outputs, in other words, the status of every port in our bank. In pseudo code this will look something like this:
GPIOA = 0x12
AllOfBankA = read_from_i2c_bus(device_address,GPIOA)
You will get a byte back which corresponds to 8 bits, the 8 ports. If you get back for example 0xF5 that means your 4 outputs are high (0xF0) which is what you would expect since we just set these and the 4 inputs have status 0x05 which means the first input (GPA0) is high (bit 0 which corresponds to a 1) and GPA2, bit 2 which corresponds to a 4. GPA1 and GPA3 are low in this example. If your are unfamiliar with hexadecimal and bits, there is plenty on the internet to learn this (example link).
Well, this covers the basics. We can determine which ports are input and which are output and we can set a value to each and read the value from them.
In the MCP23017 chip there are 2 banks, A and B and each have seperate addresses to use but remember the device address (in our example 0x20) is the same. To summarize the addresses:
Determine which ports are input and output:
IODIRA (0x00) and IODIRB (0x01)
Set the output ports:
OLATA (0x14) and OLATB (0x15)
To read both output and input ports:
GPIOA (0x12) and GPIOB (0x13)
This covers the very basics of the MCP23017.
Read the next steps in I2C and MCP23017, part 2