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:
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.
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:
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.
If we want to change an output pins value we can use the Write() method.
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.
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.
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.