Windows 10 IoT on Raspberry PI with MCP23017 and MCP3208 Chips

Recently I started building small demo circuits on my Raspberry Pi 2 and programming them using Window 10 IoT.   One small issue that I ran into is that the Raspberry Pi 2 does not include any pins for handling analog inputs.

Reading Analog Values with the Raspberry Pi 2.
To work with analog sensors you need an analog to digital convertor.   One popular chip that I ran across is called the MCP3208 which is a 8 channel 12-bit analog to digital convertor that can connect to the Raspberry Pi using the SPI protocol.

With this chip I can read up to 8 different analog values and convert them into a 12-bit value. This gives me a range of 0 to 4095, decimal, for each of the analog inputs. It isn’t high precision, however, it works for many of the basic circuits I am working with.

Using the SPI protocol is not difficult, however, I wanted to abstract out the complexities to make it quicker when I write code for the Raspberry Pi. With that in mind I created a MCP3208 class that has a few simple methods for initializing the chip and reading values from the different channels.

To get started using this class, you just create an instance of it.

private MCP3208 mcp3208 = new MCP3208();

We then need to initialize the chip by using the following command:

 Await mcp3208.Init();

After the chip is initialized we can begin to read values from each of the channels. Below is an example of reading channel 1 and channel 2.

 double channel0Val = mcp3208.ReadChannel(MCP3208.Channel.CH0);

double channel1Val = mcp3208.ReadChannel(MCP3208.Channel.CH1);

I have also included a method that can convert the returned value into an approximate voltage that the channel is reading. Below is an example of how to get the approximate voltage level on channel 0. This assumes that the maximum voltage on the input pin of channel 0 is 3.3 volts.

double channel0Volts = mcp3208.ConvertToVolts(3.3, mcp3208.ReadChannel(MCP3208.Channel.CH0));

When you are done using the class, run the dispose method for clean-up.

Mcp3208.Dispose();

One final method I included would not be using in an actual program, but was included to help wiring the chip to the Raspberry Pi. If the method GetWiringInfo() is called it will return a string with the pin out information for the chip and a description on what pins to hook to the Raspberry Pi.

Adding additional I/O Ports to the Raspberry Pi 2
Once you start building larger circuits with the Raspberry Pi, you may run into a situation where you run out of GPIO ports. To overcome this limitation we can use a MCP23017, a 16-bit I/O Expander with Serial Interface. The MCP23017 interfaces with the Raspberry Pi using the I2C protocol.   With this chip I can add an additional 16 GPIO pins to my Raspberry Pi.   It is also possible to chain multiple MCP23017 chips together to give you 128 additional GPIO pins!

Communicating from the Raspberry Pi to the MCP23017 is a bit more complex than the MCP3208 analog to digital convertor. First the MCP23017 has several registers that have to be properly set to configure chip options and to also set GPIO pin directions (input / output). Instead of having to remember all of the registers, hex codes, and such, I decided to create a simple class to make using the chip much more simpler.   This class currently only works with a single MCP23017 chip. I have not started to look at chaining the chips together yet.

To get started you create an instance of the class as shown below.

 private MCP23017 i2cPortExpander = new MCP23017();

We can then initialize the chip by calling the following method:

 await i2cPortExpander.Init();

Now we need to set the drive mode for the pins we will be using on the chip. We can do this by passing in the pin and pin mode to the SetDriveMode() method as shown below.

i2cPortExpander.SetDriveMode(MCP23017.Pin.GPA0, MCP23017.PinMode.Output);

i2cPortExpander.SetDriveMode(MCP23017.Pin.GPB7, MCP23017.PinMode.Input);

If we want to change an output pins value we can use the Write() method.

i2cPortExpander.Write(MCP23017.Pin.GPA0, MCP23017.PinValue.High);

To read from an input pin we use the Read() method.

bool pinValue = i2cPortExpander.Read(MCP23017.Pin.GPB7);

 There may be times when we need to react to a changing input pin value, like the press of a button. Instead of creating a loop that keeps calling the Read() method we can utilize the interrupt capability of the MCP23017 chip.   When values are changed on monitored input pins the chip will notify the Raspberry Pi by sending a signal to one of the Raspberry Pi’s GPIO ports. We can then have our program react by asking the MCP23017 for information on which pin changed it’s value and then we can read the value of that pin. To get started with interrupts, we need to tell the MCP23017 to send the interrupt signal to the Raspberry Pi whenever a monitored pin changes value. This can be done using the SetInterruptOnChange() method, passing in the pin number and true if we want to monitor the pin or false if we no longer wish to have the pin monitored.

i2cPortExpander.SetInterruptOnChange(MCP23017.Pin.GPB7, true);

This allows the use of the ValueChanged event of a Raspberry Pi input pin to run code whenever any monitored pin on the MCP23017 chip changes value.

The GetChangedPins() method on the MCP23017 class returns a list of pins which changed and caused the interrupt to trigger. Looping through these pins we can read their value and then take some action.   By utilizing interrupts we do not need to have a program loop occurring just to check the status of a pin, such as a button.

Just like the MCP3208, I have also included the GetWiringInfo() method to assist with the wiring of the chip to the Raspberry Pi.

Download
I am making the source code available under the GNU GPL license. You can download the source code which includes both the MCP23017 and the MCP3208 classes.   You will need Visual Studio 2015 to open the file. You will also need to have the Windows IoT Core software installed which is included with the Windows 10 IoT download for the Raspberry Pi.

NOTE: I am not responsible for any damages that occur through the use or misuse of the software. I have made efforts to ensure the code works as expected and that the wiring information is correct. You should review the code and the wiring prior to powering up your circuit to prevent any damage. I do not provide support for this software. You can contact me through my blog if you have any questions or suggestions for changes to the code.

14 thoughts on “Windows 10 IoT on Raspberry PI with MCP23017 and MCP3208 Chips”

    1. Hi Adorian,
      Could you please show the sample? I Can get it to trigger ok with one button to the MCP23017 but as soon as I need several I don’t get it to trigger the PieEvent on the correct Port without using several GPIO on the Raspberry.

  1. Mike, the source code project link is unavailable. Can you please provide a working link for the code download?
    Thanks in advance

  2. Mike, I’ve been testing the libraries for the mcp23017 and I have an issue:
    When I do the SetInterruptOnChange for multiple pins, the Raspberry GPIO pin value change event is raised multiple times (connected to INTB on the MCP23017) when, for example, GPB0 pin value changes. Also, the GetChangedPins brings some wrong values…
    If I call SetInterruptOnChange only for one pin, it works perfectly.

    Did you ever got any problem like this?

  3. Mike, Thank you for samples .
    I found an error in MCP3208 SPI clock frequency setting. It should be less than 1Mhz not 10Mhz. If chip is clocked too fast it reads half of voltage value.

    1. Thank you for catching that. I am going to be making a couple updates and posting the source up to GitHub to make it easier for everyone to make updates and submit pull requests for updates.

  4. Hi Mike,
    Thanks for a perfect Introduction to the MCP23017 and the Helper-classes. WH n Iuse this including the trigger event for 1 button that will light one LED it works perfectly from the MCP23017 but…

    I put up a little schematic with 2 input button to trigger two ports of the MCP23017, but I’m not sure how to get both these buttons to trigger the raspberrys ValueChanged-event for one input-pin. Because if I connect both these inputs to the pin that trigger the raspberry-event, each button will trigger so GetChangedPins returns both pins.

    How would each input of the MCP23017 be able to individually trigger the respberrys PIN that have the ValueChange event without trigging any of the other input pins?

    I’m sure that I miss something.

  5. The source code has been moved to GitHub: https://github.com/mphacker/RaspberryPiComponents. I have not had a chance to work on any updates but I do plan to look into a few of the questions here. If you have made any updates or fixes you think should be included in the original source, please fork the code on GitHub, make your updates, and then submit a pull request.

  6. Mike Hacker, thanks for this code. The only thing I don’t see in it is optionally setting up the mirroring mode so that the interrupt pins both fire instead of firing per side.

Leave a Reply