Introduction: A Gpio Expander Board for Robot
This Instructable is about making a GPIO port expander.
In another Instructable, we go over steps to make a tracked robot, the Agent 390 from Servocity.
Once we have gotten past the first phase of getting the robot to move, we are ready to have it do more.
We want the robot to be able to interact with its environment and hopefully do some useful (or at least cool) tasks. For that, it needs many assorted sensors and controls.
The Arduino and Raspberry Pi families (such as the RPi 3b/3b+) have many GPIO pins to help us, but one could still easily run out of ports when adding sensors and controls to a mobile robot.
In my particular case, thinking very briefly, very superficially for only the immediately required sensors, I came up the following list:
- At least four acoustic sonar sensors, one for each corner of the robot
- At least two infrared sensors, one for the front and one for the rear
- Perhaps one or more servo motors for some or all of the above sensors
- An emergency-stop watchdog circuit to quickly shutdown any robot movment
- One or more voltage-monitoring circuits
- I would need several LED indicators to give a quick status, such as low-voltage, or a fault occurred, or maybe that the robot has encountered an obstacle. Each LED could take up a GPIO port
- Perhaps one or two buzzers
Without delving into any details of these sensors and controls, we could safely say that we would need around two GPIO pins per sensor. So let's say we want 12 components plus maybe a few LEDs. That could be as much as 30 ports or even more.
The Raspberry Pi 3b/3b+ has a 40-pin header to expose GPIOs, but I counted 10 pins that were either ground or 3.3V/5V. Then there are those that have more than one purpose. So, we can easily run out of ports.
I come at this robot-making project with the mind frame of not wanting to be caught short or under-resourced.
After researching various boards and sites where I could purchase them, I realized I had pretty much all the parts already, can I could make my own custom board to my liking.
To those who have gone as far as to etch their own PCB, my hat is off to you - congratulations. Think there are many who aren't going to be able to do that or are not inclined to do so.
So this Instructable is about making your own breakout board using readily-available parts, such as from Amazon, Servocity, RobotShop, SuperDroidRobots, Adafruit, etc.
Step 1: Design, Breadboard, Test Prep
There are several ways to expand the number of available GPIO ports for the Raspberry Pi.
One uses I2C, another one uses SPI, and there are others.
There are port-expander ICs available that interface with the Raspberry via the SPI protocol, but for this Instructable, we'll be using a I2C cousin, the MCP23017.
By the way, there are several how-tos out there to wire and use this chip with the Raspberry. I didn't see much in the way of doing multiple-chips, and also there seemed to be a bit of confusion with voltage levels, since the Raspberry's GPIO pins are 3.3V, and yet many sensors are either 5V, or give better results at 5V even if they technically can function at 3.3V. Others "may" work accepting 3.3V inputs but that can become an issue.
Thus, I decided to write this Instructable.
The first step is to design your circuit. The wiring diagram image in this section gives us a basic, but very usable starting point to adding sensors to our robot.
We begin with two MCP23017 chips, and we add in a very key breakout board that makes it a cinch to go between 5V and 3.3V. It is a level-shifter. I got mine from Amazon. A XCSOURCE 5PCS IIC I2C Logic Level Converter Bi-Directional Module 5V to 3.3V TE291.
There were some mixed reviews, but I have not had any problems.
The next key point is addressing. You'll need the Raspberry Pi to distinguish between the two chips. Notice in the wiring diagram that the "A0,A1,A2" lines are all connected to ground on both chips, with only one exception, and that one is tied to 5V.
Also notice that the MCP23017 chips in this circuit know nothing of 3.3V. Just 5V and ground. The level-shifter "de-couples" the 3.3V world from the 5V world.
I don't think one should go from "oh good here's a circuit diagram or idea", straight to constructing the final board that you'll be using. Breadboarding it first is a good practice.
I got tired of faulty breadboard connections and after trying several different ones, the Elenco Breadboard 3742 Total Contact Points has been reliable. Have used it for many months.
I used the very top bar as a power bus and wired all the "red" connections together, and all the "blues" together. Then I wired the top bus bar's blue to the black terminal, and the top bus bar's red to the red terminal.
Not shown in the diagram, but may be a good idea to put some capacitors across the 5V and ground rails near these chips. I got some from Joe Knows Electronics 33 Value 645 Piece Capacitor Kit.
You'll probably want some jumper wires.
The Raspberry Pi Canakit came with a special GPIO breakout ribbon-type header board that connects from the Pi's header pins, onto a breadboard. I did not attach it to the same breadboard as the one from Elenco, although you could use that one if you like. I just prefer to keep all of the Elenco board space for circuits.
If you use a similar setup with two separate breadboards, make sure that the two boards have the same ground.
I use a separate power source from the what powers the Pi, and what powers the Elenco breadboard with circuits. I hook that separate power supply to the red and black terminals of the main Elenco breadboard. I really don't put anything else, as far as components are concerned, on the Raspberry breadboard.
So how to test it? I can think of a few different ways.
- Probe newly available GPIO pins with a voltmeter, expecting logic HI and LOW as you control the ports via the Raspberry
- Do the same, but use an inexpensive logic probe that buzzes and lights, depending on logic level
- Use an oscilloscope to do the same
- Or more fun - add some LEDs
Choose a few GPIO pins from each of the MCP23017s, add an LED and a resistor (1K?) in series, from 5V to the chip's pin. You don't need a bright LED.. just something to indicate things are working.
Step 2: Prepare the Raspberry
Before we can use our new circuit, we need to make sure that the Raspberry can detect and communicate using the i2c protocol.
So, at the Raspberry shell prompt, type "sudo raspi-config".
A blue screen with a while menu should appear. Using arrow keys, move down to "Interfacing Options"
Hit the Enter key.
Move down to "I2C". Hit the Enter key. You should see a message like "Would you like the ARM I2C interface to be enabled?" Use the arrow keys or Enterto move to "Yes".
Hit the Enter key.
You should see some message like "The ARM I2C interface is enabled".
Hit the Enter key.
You should be back at the main menu. You should be able to use the Tab key to move to "Finish".
Hit the Enter key.
Then you're back at the shell prompt.
The next step is to install i2c-tools. "sudo apt-get install -y i2c-tools".
Ok, we've done enough to move on to some initial testing.
Step 3: Let's Do Some Testing
By now we should be set to do something with our newly acquired GPIO ports.
I found a good tutorial that explains the basics of i2c using a command-line utility from the i2c-tools that we installed, so I won't go into any detail here.
Once you get the hang of writing to the new ports, let's do something a bit more interesting.
I wrote a very simple, straightforward Bash shell script that uses the i2cset utitlity to control the LEDs in a pattern.
Here is the code:
#!/bin/bash
#########################################
# turns on every other LED, in two sets
#creating an alternating pattern
#there is a small delay inbetween.
#the loop runs 10 times.
# just before the script ends, it turns
# all the LEDs off
.#
# since in my circuit, the LEDs go from
# +5V to the GPIO output, a logic LOW
# (0v) turns them on, and a logic HIGH
# (+5V) turns them off.
#########################################
for i in 1 2 3 4 5 6 7 8 9 0;
do
sudo i2cset -y 1 0x20 0x12 0xAA;
sleep 0.2;
sudo i2cset -y 1 0x20 0x12 0x55;
sleep 0.2;
done;
sleep 0.2;
sudo i2cset -y 1 0x20 0x12 0xff;
You can see the action in th video.
Step 4: Do the Software Thing
Ok, let's move on. Let's do something similar to what we did on the command-line or with the Bash shell script.
Here is a python script that does the alternating LED pattern, but without a loop:
import smbus
import time
bus = smbus.SMBus(1)
print("initialize")
bus.write_byte_data(0x20,0x00,0x00) time.sleep(2)
print("make sure all off")
bus.write_byte_data(0x20,0x12,0xff)
time.sleep(2)print("pattern 1")
bus.write_byte_data(0x20,0x12,0x55) time.sleep(1)
print("pattern 2")
bus.write_byte_data(0x20,0x12,0xAA) time.sleep(1)print("pattern 1")
bus.write_byte_data(0x20,0x12,0x55) time.sleep(1)
print("pattern 2")
bus.write_byte_data(0x20,0x12,0xAA) time.sleep(2)
print("all on")
bus.write_byte_data(0x20,0x12,0x00) time.sleep(2)
print("all off")
bus.write_byte_data(0x20,0x12,0xff) time.sleep(1) print("done")
Please take a look at the video for more details.
Step 5: Add to the Circuit, More Testing, More Software
Now that we feel confident the circuit works, and we can control it with the Raspberry Pi, we are ready to add the second port expander chip. Wiring is just a bit easier, since we don't have to add anything related to the Raspberry Pi. Just add power and ground to the chip, then connect the same I2C signal lines from the new chip to the first one, and finally wire the the "A0,A1,A2" lines. All go to ground except one (I chose A0), which goes to +5V, and that distinguishes it from the first chip.
Using the i2cdetect should now show two devices, one is "20", the other "21".
Please take a look at the video for lots of details and code.
Here is yet another program to control the LEDs, this time it is written in C/C++, and it uses the wiringPi library.
#include
#include #include
int main (void) { // abitrarily chose 100, and 200 at starting pin numbers
int mcp23017_chip_1_pin_numbering_starts_at_100 = 100; int mcp23017_chip_1_i2c_address = 0x20;
int mcp23017_chip_2_pin_numbering_starts_at_200 = 200; int mcp23017_chip_2_i2c_address = 0x21;
// initialize the circuit , software, and pins as outputs. wiringPiSetup () ; mcp23017Setup ( mcp23017_chip_1_pin_numbering_starts_at_100 , mcp23017_chip_1_i2c_address); mcp23017Setup ( mcp23017_chip_2_pin_numbering_starts_at_200 , mcp23017_chip_2_i2c_address);
for (int i = 0 ; i < 10 ; ++i) { pinMode(mcp23017_chip_1_pin_numbering_starts_at_100 + i, OUTPUT); pinMode(mcp23017_chip_2_pin_numbering_starts_at_200 + i, OUTPUT); }
printf ("Raspberry Pi - MCP23017 Test\n") ;
/); }
delay (100) ;
//make LED ligthing 'scroll' left to right //); } for (int i = 0 ; i < 8 ; ++i) { digitalWrite(mcp23017_chip_2_pin_numbering_starts_at_200 + i, 0x1); delay(50); } for (int i = 0 ; i < 8 ; ++i) { digitalWrite(mcp23017_chip_1_pin_numbering_starts_at_100 + i, 0x1); delay(50); }); }
return 0 ; }
Step 6: The Dreaded Inevitable - Commitment Time - the Real Wiring
So we have tested our GPIO expander circuit. Perhaps we got really excited and began adding some of our sensors.
I know I did, because one of my first requirements or must-haves for the robot was to add an emergency-stop feature.
I needed a way to test a circuit I was designing for use as a watchdog circuit, so I used this new expander circuit mockup.
But, come on people - strapping your breadboard onto your real project (robot or whatever) is not how things are done, ok? I've seen that a lot.
For one thing, that means I can't use my breadboard for anything else because it's busy doing its thing on the robot.
The reason for the "Dreaded" in the title is that constructing a real circuit on a real board isn't quite as easy as breadboarding. Also, it can be quite tedious. It's time-consuming. Very prone to mistakes.
The "Commitment" means as we make the connections (solder?) and choose our real components and parts, cut our wires, etc, we're more and more committed to the result, whereas a breadboard is easily torn down, changed, re-configured, and the like.
But perhaps we can mitigate some of these negatives.
I'm going to try to use sockets, connector, and terminals as much as possible, but at the same time I want to have solid electrical contact.
Step 7: Test the Wired Circuits
We've assembled and soldered the circuit. It's good to try to test at each step. Look for shorts where there shouldn't be any, and for open-circuits where there should be shorts.
At minimum, for each individual circuit, test for shorts across the positive and ground rails.
The entire thing as it stands now, consists of:
- a 16-bit bi-directional 3V-to-5V level-shifter bus. It's composed of four 4-bit buses. You can safely connect the Raspberry's GPIO ports directly to it, and it will convert those signals to 0-5V on the other side. I've tested one of those 4-bit circuits using SPI at very high rates, and even was able to do some voice-recognition (using an MCP3008), so they're fast enough I think.
- two (haven't yet completely wired the 2nd one) 16-bit I2C buses (MCP23017), running at 5V.
- an E-Stop watchdog circuit that controls a relay and expects a 7ms on / 14ms period pulse in order to not fire.
- a general-purpose board that can handle LEDS, buzzers, some chips, perhaps switches.
We want to wire them together according to the schematic we've been discussing, and a good next test would be to connect them together with some male-male, male-female jumper-wires and tighten those with the terminal blocks.
We should be able to re-use our test programs from earlier.
You may think, most of these boards are really nothing more than the discrete original components that we breadboarded. We could have put all or most on a single perfboard. However, I was thinking that is too restrictive. I may want to move things around. I may want to start over and re-use them in another way.
The way those smaller boards are at the moment, they are like building-blocks. They each have a clear, distinct purpose. To put it another way, they're de-coupled from each other, and all we need to do is respect their various inputs / outputs and voltage-levels, but we're not tied to a specific overall set of circuits for one specific application.
The downside of course is - all these little boards. But that can be mitigated. They're now sitting on standoffs, and can be easily bolted on plastic board, as an example. If we don't like how it is later, we can drill new holes, and move the circuits around. We can cut the main plastic board in half if we want.
We have similar advantages of breadboarding, but with a more permanent, stronger electrical connection.
One thing we'll have to keep in mind, however, is that electrical noise might be more easily introduced with longer wires.
There is one more board missing - or perhaps we call it something else. A clean, clear way to distribute power and ground. I think we'll tackle that one once we're ready to mount everything on the plastic hardboard and we've decided how everything is placed in relation to on another, and also to how the robot is currently laid out.
Be the First to Share
Recommendations
